1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-04-24 16:42:36 +02:00

More fixes and additions to Day1 Morning (#269)

* Corrections around slices and string slices, also slight improvements to the language.

* Explained some of the confusing details in the functions example.

* Added a speaker note (hinting at `Into`  as a complement to generics).

* Exclude the clarification code snippet from build testing.

* Update functions-interlude.md

Do not mention explicit section numbers as they may change.
This commit is contained in:
Marko Zagar 2023-01-24 14:20:18 +00:00 committed by GitHub
parent b064642b02
commit 10ca7b1b2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 16 deletions

View File

@ -21,3 +21,11 @@ fn main() {
println!("cash prize: {}", pick_one(500, 1000)); println!("cash prize: {}", pick_one(500, 1000));
} }
``` ```
<details>
* When using generics, the standard library's `Into<T>` can provide a kind of limited
polymorphism on argument types. We will see more details in a later section.
</defails>

View File

@ -11,7 +11,7 @@ fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
if rhs == 0 { if rhs == 0 {
return false; // Corner case, early return return false; // Corner case, early return
} }
lhs % rhs == 0 // The last expression is the return value lhs % rhs == 0 // The last expression in a block is the return value
} }
fn fizzbuzz(n: u32) -> () { // No return value means returning the unit type `()` fn fizzbuzz(n: u32) -> () { // No return value means returning the unit type `()`
@ -24,8 +24,8 @@ fn fizzbuzz(n: u32) -> () { // No return value means returning the unit type `(
} }
fn fizzbuzz_to(n: u32) { // `-> ()` is normally omitted fn fizzbuzz_to(n: u32) { // `-> ()` is normally omitted
for n in 1..=n { for i in 1..=n {
fizzbuzz(n); fizzbuzz(i);
} }
} }
``` ```
@ -34,7 +34,21 @@ fn fizzbuzz_to(n: u32) { // `-> ()` is normally omitted
* We refer in `main` to a function written below. Neither forward declarations nor headers are necessary. * We refer in `main` to a function written below. Neither forward declarations nor headers are necessary.
* Declaration parameters are followed by a type (the reverse of some programming languages), then a return type. * Declaration parameters are followed by a type (the reverse of some programming languages), then a return type.
* The last expression in a function body becomes the return value. Simply omit the `;` at the end of the expression. * The last expression in a function body (or any block) becomes the return value. Simply omit the `;` at the end of the expression.
* Some functions have no return value, and output the 'unit type', `()`. The compiler will infer this if the `-> ()` return type is omitted. * Some functions have no return value, and return the 'unit type', `()`. The compiler will infer this if the `-> ()` return type is omitted.
* The range expression in the `for` loop in `fizzbuzz_to()` contains `=n`, which causes it to include the upper bound.
* The `match` expression in `fizzbuzz()` is doing a lot of work. It is expanded below to show what is happening.
(Type annotations added for clarity, but they can be elided.)
```rust,ignore
let by_3: bool = is_divisible_by(n, 3);
let by_5: bool = is_divisible_by(n, 5);
let by_35: (bool, bool) = (by_3, by_5);
match by_35 {
// ...
```
</details> </details>

View File

@ -19,16 +19,18 @@ fn main() {
* We create a slice by borrowing `a` and specifying the starting and ending indexes in brackets. * We create a slice by borrowing `a` and specifying the starting and ending indexes in brackets.
* If the slice starts at index 0, Rust’s range syntax means we can drop the starting index. * If the slice starts at index 0, Rust’s range syntax allows us to drop the starting index, meaning that `&a[0..a.len()]` and `&a[..a.len()]` are identical.
* The same is true for the last index, so `&a[2..a.len()]` and `&a[2..]` are equal. * The same is true for the last index, so `&a[2..a.len()]` and `&a[2..]` are identical.
* `s` is a reference to a slice of `i32`s. Notice that the type of `s` no longer mentions the array length. This allows us to performing computations on slices of different sizes. * To easily create a slice of the full array, we can therefore use `&a[..]`.
* `s` is a reference to a slice of `i32`s. Notice that the type of `s` (`&[i32]`) no longer mentions the array length. This allows us to perform computation on slices of different sizes.
* Slices always borrow from another object. In this example, `a` has to remain 'alive' so we can take a slice from it. * Slices always borrow from another object. In this example, `a` has to remain 'alive' (in scope) for at least as long as our slice.
* The question about modifying `a[3]` can spark an interesting discussion, but the answer is that for memory safety reasons * The question about modifying `a[3]` can spark an interesting discussion, but the answer is that for memory safety reasons
you cannot do it through `a` after you created a slice. But you can read the data from both `a` and `s` safely. you cannot do it through `a` after you created a slice, but you can read the data from both `a` and `s` safely.
More details will be explained in the borrow checker section. More details will be explained in the borrow checker section.
</details> </details>

View File

@ -24,19 +24,21 @@ Rust terminology:
<details> <details>
* `&str` introduces a string slice, which is an immutable reference to UTF-8 encoded string data stored in a block of memory. String literals (`”Hello”`), are stored in the program’s binary. * `&str` introduces a string slice, which is an immutable reference to UTF-8 encoded string data
stored in a block of memory. String literals (`”Hello”`), are stored in the program’s binary.
* Rust’s `String` type is a wrapper around a vector of bytes. As with a `Vec<T>`, it is owned. * Rust’s `String` type is a wrapper around a vector of bytes. As with a `Vec<T>`, it is owned.
* As with many other types * As with many other types `String::from()` creates a string from a string literal; `String::new()`
creates a new empty string, to which string data can be added using the `push()` and `push_str()` methods.
* `String::from` creates a string from a string literal; `String::new` creates a new empty string, to which string data can be added using the `to_string` method. * The `format!()` macro is a convenient way to generate an owned string from dynamic values. It
accepts the same format specification as `println!()`.
* The `push_str` method appends a string slice to the string.
* You can borrow `&str` slices from `String` via `&` and optionally range selection. * You can borrow `&str` slices from `String` via `&` and optionally range selection.
* For C++ programmers: think of `&str` as `const char*` from C++, but the one that always points * For C++ programmers: think of `&str` as `const char*` from C++, but the one that always points
to a valid string in memory. Rust `String` is a rough equivalent of `std::string` from C++ (main difference: it can only contain UTF-8 encoded bytes and will never use a small-string optimization). to a valid string in memory. Rust `String` is a rough equivalent of `std::string` from C++
(main difference: it can only contain UTF-8 encoded bytes and will never use a small-string optimization).
</details> </details>