From 0a3135500400634753be42f5369ea0cd3585671e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Sampaio?= Date: Thu, 26 Sep 2024 21:53:15 +0100 Subject: [PATCH 1/4] Convert ch04 to `` --- src/ch04-01-what-is-ownership.md | 24 ++++++++++++------------ src/ch04-02-references-and-borrowing.md | 4 ++-- src/ch04-03-slices.md | 15 +++++++-------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/ch04-01-what-is-ownership.md b/src/ch04-01-what-is-ownership.md index 5249c2dd77..b8f14fd49e 100644 --- a/src/ch04-01-what-is-ownership.md +++ b/src/ch04-01-what-is-ownership.md @@ -113,12 +113,13 @@ hardcoded into the text of our program. The variable is valid from the point at which it’s declared until the end of the current *scope*. Listing 4-1 shows a program with comments annotating where the variable `s` would be valid. ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-01/src/main.rs:here}} ``` -Listing 4-1: A variable and the scope in which it is -valid + In other words, there are two important points in time here: @@ -239,12 +240,13 @@ we’ve allocated on the heap. Let’s explore some of those situations now. Multiple variables can interact with the same data in different ways in Rust. Let’s look at an example using an integer in Listing 4-2. ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-02/src/main.rs:here}} ``` -Listing 4-2: Assigning the integer value of variable `x` -to `y` + We can probably guess what this is doing: “bind the value `5` to `x`; then make a copy of the value in `x` and bind it to `y`.” We now have two variables, `x` @@ -429,14 +431,13 @@ assigning a value to a variable. Passing a variable to a function will move or copy, just as assignment does. Listing 4-3 has an example with some annotations showing where variables go into and out of scope. -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-03/src/main.rs}} ``` -Listing 4-3: Functions with ownership and scope -annotated + If we tried to use `s` after the call to `takes_ownership`, Rust would throw a compile-time error. These static checks protect us from mistakes. Try adding @@ -449,14 +450,13 @@ Returning values can also transfer ownership. Listing 4-4 shows an example of a function that returns some value, with similar annotations as those in Listing 4-3. -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-04/src/main.rs}} ``` -Listing 4-4: Transferring ownership of return -values + The ownership of a variable follows the same pattern every time: assigning a value to another variable moves it. When a variable that includes data on the @@ -471,13 +471,13 @@ from the body of the function that we might want to return as well. Rust does let us return multiple values using a tuple, as shown in Listing 4-5. -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-05/src/main.rs}} ``` -Listing 4-5: Returning ownership of parameters + But this is too much ceremony and a lot of work for a concept that should be common. Luckily for us, Rust has a feature for using a value without diff --git a/src/ch04-02-references-and-borrowing.md b/src/ch04-02-references-and-borrowing.md index ea2d8d2029..9eb8c0057b 100644 --- a/src/ch04-02-references-and-borrowing.md +++ b/src/ch04-02-references-and-borrowing.md @@ -67,13 +67,13 @@ to give it back. You don’t own it. So, what happens if we try to modify something we’re borrowing? Try the code in Listing 4-6. Spoiler alert: it doesn’t work! -Filename: src/main.rs + ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-06/src/main.rs}} ``` -Listing 4-6: Attempting to modify a borrowed value + Here’s the error: diff --git a/src/ch04-03-slices.md b/src/ch04-03-slices.md index 7f8c9b7af2..0d94433652 100644 --- a/src/ch04-03-slices.md +++ b/src/ch04-03-slices.md @@ -21,14 +21,13 @@ ownership, so this is fine. But what should we return? We don’t really have a way to talk about *part* of a string. However, we could return the index of the end of the word, indicated by a space. Let’s try that, as shown in Listing 4-7. -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:here}} ``` -Listing 4-7: The `first_word` function that returns a -byte index value into the `String` parameter + Because we need to go through the `String` element by element and check whether a value is a space, we’ll convert our `String` to an array of bytes using the @@ -73,14 +72,13 @@ because it’s a separate value from the `String`, there’s no guarantee that i will still be valid in the future. Consider the program in Listing 4-8 that uses the `first_word` function from Listing 4-7. -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-08/src/main.rs:here}} ``` -Listing 4-8: Storing the result from calling the -`first_word` function and then changing the `String` contents + This program compiles without any errors and would also do so if we used `word` after calling `s.clear()`. Because `word` isn’t connected to the state of `s` @@ -257,12 +255,13 @@ A more experienced Rustacean would write the signature shown in Listing 4-9 instead because it allows us to use the same function on both `&String` values and `&str` values. ++ ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-09/src/main.rs:here}} ``` -Listing 4-9: Improving the `first_word` function by using -a string slice for the type of the `s` parameter + If we have a string slice, we can pass that directly. If we have a `String`, we can pass a slice of the `String` or a reference to the `String`. This From df49c42aac78f37e42a26f488f3fe7a506311a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Sampaio?= Date: Thu, 26 Sep 2024 22:09:01 +0100 Subject: [PATCH 2/4] The ones without listing numbers --- src/ch04-02-references-and-borrowing.md | 20 +++++++++++++++----- src/ch04-03-slices.md | 12 +++++++++--- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/ch04-02-references-and-borrowing.md b/src/ch04-02-references-and-borrowing.md index 9eb8c0057b..04dd601c7d 100644 --- a/src/ch04-02-references-and-borrowing.md +++ b/src/ch04-02-references-and-borrowing.md @@ -12,12 +12,14 @@ particular type for the life of that reference. Here is how you would define and use a `calculate_length` function that has a reference to an object as a parameter instead of taking ownership of the value: -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-07-reference/src/main.rs:all}} ``` + + First, notice that all the tuple code in the variable declaration and the function return value is gone. Second, note that we pass `&s1` into `calculate_length` and, in its definition, we take `&String` rather than @@ -89,12 +91,14 @@ allowed to modify something we have a reference to. We can fix the code from Listing 4-6 to allow us to modify a borrowed value with just a few small tweaks that use, instead, a *mutable reference*: -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-09-fixes-listing-04-06/src/main.rs}} ``` + + First we change `s` to be `mut`. Then we create a mutable reference with `&mut s` where we call the `change` function, and update the function signature to accept a mutable reference with `some_string: &mut String`. This makes it very @@ -104,12 +108,14 @@ Mutable references have one big restriction: if you have a mutable reference to a value, you can have no other references to that value. This code that attempts to create two mutable references to `s` will fail: -Filename: src/main.rs + ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-10-multiple-mut-not-allowed/src/main.rs:here}} ``` + + Here’s the error: ```console @@ -198,12 +204,14 @@ reference to the data does. Let’s try to create a dangling reference to see how Rust prevents them with a compile-time error: -Filename: src/main.rs + ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-14-dangling-reference/src/main.rs}} ``` + + Here’s the error: ```console @@ -222,12 +230,14 @@ for it to be borrowed from Let’s take a closer look at exactly what’s happening at each stage of our `dangle` code: -Filename: src/main.rs + ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-15-dangling-reference-annotated/src/main.rs:here}} ``` + + Because `s` is created inside `dangle`, when the code of `dangle` is finished, `s` will be deallocated. But we tried to return a reference to it. That means this reference would be pointing to an invalid `String`. That’s no good! Rust diff --git a/src/ch04-03-slices.md b/src/ch04-03-slices.md index 0d94433652..69fb35f82d 100644 --- a/src/ch04-03-slices.md +++ b/src/ch04-03-slices.md @@ -174,12 +174,14 @@ let slice = &s[..]; With all this information in mind, let’s rewrite `first_word` to return a slice. The type that signifies “string slice” is written as `&str`: -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-18-first-word-slice/src/main.rs:here}} ``` + + We get the index for the end of the word the same way we did in Listing 4-7, by looking for the first occurrence of a space. When we find a space, we return a string slice using the start of the string and the index of the space as the @@ -205,12 +207,14 @@ string. Slices make this bug impossible and let us know we have a problem with our code much sooner. Using the slice version of `first_word` will throw a compile-time error: -Filename: src/main.rs + ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-19-slice-error/src/main.rs:here}} ``` + + Here’s the compiler error: ```console @@ -272,12 +276,14 @@ Methods”][deref-coercions] section of Chapter 15. Defining a function to take a string slice instead of a reference to a `String` makes our API more general and useful without losing any functionality: -Filename: src/main.rs + ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-09/src/main.rs:usage}} ``` + + ### Other Slices String slices, as you might imagine, are specific to strings. But there’s a From d7c8e047fd8ff25968a2910027dc9f0c7268d758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Sampaio?= Date: Thu, 26 Sep 2024 22:18:28 +0100 Subject: [PATCH 3/4] The ones without number, caption, and file name --- src/ch04-01-what-is-ownership.md | 36 +++++++++++++++ src/ch04-02-references-and-borrowing.md | 44 ++++++++++++++++++ src/ch04-03-slices.md | 60 +++++++++++++++++++++++++ 3 files changed, 140 insertions(+) diff --git a/src/ch04-01-what-is-ownership.md b/src/ch04-01-what-is-ownership.md index b8f14fd49e..05f5111164 100644 --- a/src/ch04-01-what-is-ownership.md +++ b/src/ch04-01-what-is-ownership.md @@ -104,10 +104,14 @@ As a first example of ownership, we’ll look at the *scope* of some variables. scope is the range within a program for which an item is valid. Take the following variable: ++ ```rust let s = "hello"; ``` + + The variable `s` refers to a string literal, where the value of the string is hardcoded into the text of our program. The variable is valid from the point at which it’s declared until the end of the current *scope*. Listing 4-1 shows a @@ -156,10 +160,14 @@ data allocated on the heap and as such is able to store an amount of text that is unknown to us at compile time. You can create a `String` from a string literal using the `from` function, like so: ++ ```rust let s = String::from("hello"); ``` + + The double colon `::` operator allows us to namespace this particular `from` function under the `String` type rather than using some sort of name like `string_from`. We’ll discuss this syntax more in the [“Method @@ -169,10 +177,14 @@ Module Tree”][paths-module-tree] in Chapter 7. This kind of string *can* be mutated: ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-01-can-mutate-string/src/main.rs:here}} ``` + + So, what’s the difference here? Why can `String` be mutated but literals cannot? The difference is in how these two types deal with memory. @@ -211,10 +223,14 @@ Rust takes a different path: the memory is automatically returned once the variable that owns it goes out of scope. Here’s a version of our scope example from Listing 4-1 using a `String` instead of a string literal: ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-02-string-scope/src/main.rs:here}} ``` + + There is a natural point at which we can return the memory our `String` needs to the allocator: when `s` goes out of scope. When a variable goes out of scope, Rust calls a special function for us. This function is called @@ -256,10 +272,14 @@ onto the stack. Now let’s look at the `String` version: ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-03-string-move/src/main.rs:here}} ``` + + This looks very similar, so we might assume that the way it works would be the same: that is, the second line would make a copy of the value in `s1` and bind it to `s2`. But this isn’t quite what happens. @@ -322,17 +342,25 @@ no longer valid. Therefore, Rust doesn’t need to free anything when `s1` goes out of scope. Check out what happens when you try to use `s1` after `s2` is created; it won’t work: ++ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/src/main.rs:here}} ``` + + You’ll get an error like this because Rust prevents you from using the invalidated reference: ++ ```console {{#include ../listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/output.txt}} ``` + + If you’ve heard the terms *shallow copy* and *deep copy* while working with other languages, the concept of copying the pointer, length, and capacity without copying the data probably sounds like making a shallow copy. But @@ -368,10 +396,14 @@ programming languages, you’ve probably seen them before. Here’s an example of the `clone` method in action: ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-05-clone/src/main.rs:here}} ``` + + This works just fine and explicitly produces the behavior shown in Figure 4-3, where the heap data *does* get copied. @@ -384,10 +416,14 @@ different is going on. There’s another wrinkle we haven’t talked about yet. This code using integers—part of which was shown in Listing 4-2—works and is valid: ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-06-copy/src/main.rs:here}} ``` + + But this code seems to contradict what we just learned: we don’t have a call to `clone`, but `x` is still valid and wasn’t moved into `y`. diff --git a/src/ch04-02-references-and-borrowing.md b/src/ch04-02-references-and-borrowing.md index 04dd601c7d..4933ec89c6 100644 --- a/src/ch04-02-references-and-borrowing.md +++ b/src/ch04-02-references-and-borrowing.md @@ -40,10 +40,14 @@ s1` Let’s take a closer look at the function call here: ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-07-reference/src/main.rs:here}} ``` + + The `&s1` syntax lets us create a reference that *refers* to the value of `s1` but does not own it. Because it does not own it, the value it points to will not be dropped when the reference stops being used. @@ -51,10 +55,14 @@ not be dropped when the reference stops being used. Likewise, the signature of the function uses `&` to indicate that the type of the parameter `s` is a reference. Let’s add some explanatory annotations: ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-08-reference-with-annotations/src/main.rs:here}} ``` + + The scope in which the variable `s` is valid is the same as any function parameter’s scope, but the value pointed to by the reference is not dropped when `s` stops being used, because `s` doesn’t have ownership. When functions @@ -79,10 +87,14 @@ Listing 4-6. Spoiler alert: it doesn’t work! Here’s the error: ++ ```console {{#include ../listings/ch04-understanding-ownership/listing-04-06/output.txt}} ``` + + Just as variables are immutable by default, so are references. We’re not allowed to modify something we have a reference to. @@ -118,10 +130,14 @@ attempts to create two mutable references to `s` will fail: Here’s the error: ++ ```console {{#include ../listings/ch04-understanding-ownership/no-listing-10-multiple-mut-not-allowed/output.txt}} ``` + + This error says that this code is invalid because we cannot borrow `s` as mutable more than once at a time. The first mutable borrow is in `r1` and must last until it’s used in the `println!`, but between the creation of that @@ -146,23 +162,35 @@ refusing to compile code with data races! As always, we can use curly brackets to create a new scope, allowing for multiple mutable references, just not *simultaneous* ones: ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-11-muts-in-separate-scopes/src/main.rs:here}} ``` + + Rust enforces a similar rule for combining mutable and immutable references. This code results in an error: ++ ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/src/main.rs:here}} ``` + + Here’s the error: ++ ```console {{#include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/output.txt}} ``` + + Whew! We *also* cannot have a mutable reference while we have an immutable one to the same value. @@ -176,10 +204,14 @@ through the last time that reference is used. For instance, this code will compile because the last usage of the immutable references, the `println!`, occurs before the mutable reference is introduced: ++ ```rust,edition2021 {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-13-reference-scope-ends/src/main.rs:here}} ``` + + The scopes of the immutable references `r1` and `r2` end after the `println!` where they are last used, which is before the mutable reference `r3` is created. These scopes don’t overlap, so this code is allowed: the compiler can @@ -214,19 +246,27 @@ compile-time error: Here’s the error: ++ ```console {{#include ../listings/ch04-understanding-ownership/no-listing-14-dangling-reference/output.txt}} ``` + + This error message refers to a feature we haven’t covered yet: lifetimes. We’ll discuss lifetimes in detail in Chapter 10. But, if you disregard the parts about lifetimes, the message does contain the key to why this code is a problem: ++ ```text this function's return type contains a borrowed value, but there is no value for it to be borrowed from ``` + + Let’s take a closer look at exactly what’s happening at each stage of our `dangle` code: @@ -245,10 +285,14 @@ won’t let us do this. The solution here is to return the `String` directly: ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-16-no-dangle/src/main.rs:here}} ``` + + This works without any problems. Ownership is moved out, and nothing is deallocated. diff --git a/src/ch04-03-slices.md b/src/ch04-03-slices.md index 69fb35f82d..9948b14862 100644 --- a/src/ch04-03-slices.md +++ b/src/ch04-03-slices.md @@ -12,10 +12,14 @@ one word, so the entire string should be returned. Let’s work through how we’d write the signature of this function without using slices, to understand the problem that slices will solve: ++ ```rust,ignore fn first_word(s: &String) -> ? ``` + + The `first_word` function has a `&String` as a parameter. We don’t want ownership, so this is fine. But what should we return? We don’t really have a way to talk about *part* of a string. However, we could return the index of the @@ -33,16 +37,24 @@ Because we need to go through the `String` element by element and check whether a value is a space, we’ll convert our `String` to an array of bytes using the `as_bytes` method. ++ ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:as_bytes}} ``` + + Next, we create an iterator over the array of bytes using the `iter` method: ++ ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:iter}} ``` + + We’ll discuss iterators in more detail in [Chapter 13][ch13]. For now, know that `iter` is a method that returns each element in a collection and that `enumerate` wraps the result of `iter` and returns each element as @@ -61,10 +73,14 @@ Inside the `for` loop, we search for the byte that represents the space by using the byte literal syntax. If we find a space, we return the position. Otherwise, we return the length of the string by using `s.len()`. ++ ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:inside_for}} ``` + + We now have a way to find out the index of the end of the first word in the string, but there’s a problem. We’re returning a `usize` on its own, but it’s only a meaningful number in the context of the `&String`. In other words, @@ -90,10 +106,14 @@ Having to worry about the index in `word` getting out of sync with the data in `s` is tedious and error prone! Managing these indices is even more brittle if we write a `second_word` function. Its signature would have to look like this: ++ ```rust,ignore fn second_word(s: &String) -> (usize, usize) { ``` + + Now we’re tracking a starting *and* an ending index, and we have even more values that were calculated from data in a particular state but aren’t tied to that state at all. We have three unrelated variables floating around that need @@ -105,10 +125,14 @@ Luckily, Rust has a solution to this problem: string slices. A *string slice* is a reference to part of a `String`, and it looks like this: ++ ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-17-slice/src/main.rs:here}} ``` + + Rather than a reference to the entire `String`, `hello` is a reference to a portion of the `String`, specified in the extra `[0..5]` bit. We create slices using a range within brackets by specifying `[starting_index..ending_index]`, @@ -133,6 +157,8 @@ src="img/trpl04-06.svg" class="center" style="width: 50%;" /> With Rust’s `..` range syntax, if you want to start at index 0, you can drop the value before the two periods. In other words, these are equal: ++ ```rust let s = String::from("hello"); @@ -140,9 +166,13 @@ let slice = &s[0..2]; let slice = &s[..2]; ``` + + By the same token, if your slice includes the last byte of the `String`, you can drop the trailing number. That means these are equal: ++ ```rust let s = String::from("hello"); @@ -152,9 +182,13 @@ let slice = &s[3..len]; let slice = &s[3..]; ``` + + You can also drop both values to take a slice of the entire string. So these are equal: ++ ```rust let s = String::from("hello"); @@ -164,6 +198,8 @@ let slice = &s[0..len]; let slice = &s[..]; ``` + + > Note: String slice range indices must occur at valid UTF-8 character > boundaries. If you attempt to create a string slice in the middle of a > multibyte character, your program will exit with an error. For the purposes @@ -193,10 +229,14 @@ the slice and the number of elements in the slice. Returning a slice would also work for a `second_word` function: ++ ```rust,ignore fn second_word(s: &String) -> &str { ``` + + We now have a straightforward API that’s much harder to mess up because the compiler will ensure the references into the `String` remain valid. Remember the bug in the program in Listing 4-8, when we got the index to the end of the @@ -217,10 +257,14 @@ compile-time error: Here’s the compiler error: ++ ```console {{#include ../listings/ch04-understanding-ownership/no-listing-19-slice-error/output.txt}} ``` + + Recall from the borrowing rules that if we have an immutable reference to something, we cannot also take a mutable reference. Because `clear` needs to truncate the `String`, it needs to get a mutable reference. The `println!` @@ -238,10 +282,14 @@ but it has also eliminated an entire class of errors at compile time! Recall that we talked about string literals being stored inside the binary. Now that we know about slices, we can properly understand string literals: ++ ```rust let s = "Hello, world!"; ``` + + The type of `s` here is `&str`: it’s a slice pointing to that specific point of the binary. This is also why string literals are immutable; `&str` is an immutable reference. @@ -251,10 +299,14 @@ immutable reference. Knowing that you can take slices of literals and `String` values leads us to one more improvement on `first_word`, and that’s its signature: ++ ```rust,ignore fn first_word(s: &String) -> &str { ``` + + A more experienced Rustacean would write the signature shown in Listing 4-9 instead because it allows us to use the same function on both `&String` values and `&str` values. @@ -289,13 +341,19 @@ makes our API more general and useful without losing any functionality: String slices, as you might imagine, are specific to strings. But there’s a more general slice type too. Consider this array: ++ ```rust let a = [1, 2, 3, 4, 5]; ``` + + Just as we might want to refer to part of a string, we might want to refer to part of an array. We’d do so like this: ++ ```rust let a = [1, 2, 3, 4, 5]; @@ -304,6 +362,8 @@ let slice = &a[1..3]; assert_eq!(slice, &[2, 3]); ``` + + This slice has the type `&[i32]`. It works the same way as string slices do, by storing a reference to the first element and a length. You’ll use this kind of slice for all sorts of other collections. We’ll discuss these collections in From 72ef94c3388001e0848f6e684115a301dcc38d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Sampaio?= Date: Sat, 28 Sep 2024 08:37:39 +0100 Subject: [PATCH 4/4] Revert "The ones without number, caption, and file name" This reverts commit d7c8e047fd8ff25968a2910027dc9f0c7268d758. --- src/ch04-01-what-is-ownership.md | 36 --------------- src/ch04-02-references-and-borrowing.md | 44 ------------------ src/ch04-03-slices.md | 60 ------------------------- 3 files changed, 140 deletions(-) diff --git a/src/ch04-01-what-is-ownership.md b/src/ch04-01-what-is-ownership.md index 05f5111164..b8f14fd49e 100644 --- a/src/ch04-01-what-is-ownership.md +++ b/src/ch04-01-what-is-ownership.md @@ -104,14 +104,10 @@ As a first example of ownership, we’ll look at the *scope* of some variables. scope is the range within a program for which an item is valid. Take the following variable: -- ```rust let s = "hello"; ``` - - The variable `s` refers to a string literal, where the value of the string is hardcoded into the text of our program. The variable is valid from the point at which it’s declared until the end of the current *scope*. Listing 4-1 shows a @@ -160,14 +156,10 @@ data allocated on the heap and as such is able to store an amount of text that is unknown to us at compile time. You can create a `String` from a string literal using the `from` function, like so: -- ```rust let s = String::from("hello"); ``` - - The double colon `::` operator allows us to namespace this particular `from` function under the `String` type rather than using some sort of name like `string_from`. We’ll discuss this syntax more in the [“Method @@ -177,14 +169,10 @@ Module Tree”][paths-module-tree] in Chapter 7. This kind of string *can* be mutated: -- ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-01-can-mutate-string/src/main.rs:here}} ``` - - So, what’s the difference here? Why can `String` be mutated but literals cannot? The difference is in how these two types deal with memory. @@ -223,14 +211,10 @@ Rust takes a different path: the memory is automatically returned once the variable that owns it goes out of scope. Here’s a version of our scope example from Listing 4-1 using a `String` instead of a string literal: -- ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-02-string-scope/src/main.rs:here}} ``` - - There is a natural point at which we can return the memory our `String` needs to the allocator: when `s` goes out of scope. When a variable goes out of scope, Rust calls a special function for us. This function is called @@ -272,14 +256,10 @@ onto the stack. Now let’s look at the `String` version: -- ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-03-string-move/src/main.rs:here}} ``` - - This looks very similar, so we might assume that the way it works would be the same: that is, the second line would make a copy of the value in `s1` and bind it to `s2`. But this isn’t quite what happens. @@ -342,25 +322,17 @@ no longer valid. Therefore, Rust doesn’t need to free anything when `s1` goes out of scope. Check out what happens when you try to use `s1` after `s2` is created; it won’t work: -- ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/src/main.rs:here}} ``` - - You’ll get an error like this because Rust prevents you from using the invalidated reference: -- ```console {{#include ../listings/ch04-understanding-ownership/no-listing-04-cant-use-after-move/output.txt}} ``` - - If you’ve heard the terms *shallow copy* and *deep copy* while working with other languages, the concept of copying the pointer, length, and capacity without copying the data probably sounds like making a shallow copy. But @@ -396,14 +368,10 @@ programming languages, you’ve probably seen them before. Here’s an example of the `clone` method in action: -- ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-05-clone/src/main.rs:here}} ``` - - This works just fine and explicitly produces the behavior shown in Figure 4-3, where the heap data *does* get copied. @@ -416,14 +384,10 @@ different is going on. There’s another wrinkle we haven’t talked about yet. This code using integers—part of which was shown in Listing 4-2—works and is valid: -- ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-06-copy/src/main.rs:here}} ``` - - But this code seems to contradict what we just learned: we don’t have a call to `clone`, but `x` is still valid and wasn’t moved into `y`. diff --git a/src/ch04-02-references-and-borrowing.md b/src/ch04-02-references-and-borrowing.md index 4933ec89c6..04dd601c7d 100644 --- a/src/ch04-02-references-and-borrowing.md +++ b/src/ch04-02-references-and-borrowing.md @@ -40,14 +40,10 @@ s1` Let’s take a closer look at the function call here: -- ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-07-reference/src/main.rs:here}} ``` - - The `&s1` syntax lets us create a reference that *refers* to the value of `s1` but does not own it. Because it does not own it, the value it points to will not be dropped when the reference stops being used. @@ -55,14 +51,10 @@ not be dropped when the reference stops being used. Likewise, the signature of the function uses `&` to indicate that the type of the parameter `s` is a reference. Let’s add some explanatory annotations: -- ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-08-reference-with-annotations/src/main.rs:here}} ``` - - The scope in which the variable `s` is valid is the same as any function parameter’s scope, but the value pointed to by the reference is not dropped when `s` stops being used, because `s` doesn’t have ownership. When functions @@ -87,14 +79,10 @@ Listing 4-6. Spoiler alert: it doesn’t work! Here’s the error: -- ```console {{#include ../listings/ch04-understanding-ownership/listing-04-06/output.txt}} ``` - - Just as variables are immutable by default, so are references. We’re not allowed to modify something we have a reference to. @@ -130,14 +118,10 @@ attempts to create two mutable references to `s` will fail: Here’s the error: -- ```console {{#include ../listings/ch04-understanding-ownership/no-listing-10-multiple-mut-not-allowed/output.txt}} ``` - - This error says that this code is invalid because we cannot borrow `s` as mutable more than once at a time. The first mutable borrow is in `r1` and must last until it’s used in the `println!`, but between the creation of that @@ -162,35 +146,23 @@ refusing to compile code with data races! As always, we can use curly brackets to create a new scope, allowing for multiple mutable references, just not *simultaneous* ones: -- ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-11-muts-in-separate-scopes/src/main.rs:here}} ``` - - Rust enforces a similar rule for combining mutable and immutable references. This code results in an error: -- ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/src/main.rs:here}} ``` - - Here’s the error: -- ```console {{#include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/output.txt}} ``` - - Whew! We *also* cannot have a mutable reference while we have an immutable one to the same value. @@ -204,14 +176,10 @@ through the last time that reference is used. For instance, this code will compile because the last usage of the immutable references, the `println!`, occurs before the mutable reference is introduced: -- ```rust,edition2021 {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-13-reference-scope-ends/src/main.rs:here}} ``` - - The scopes of the immutable references `r1` and `r2` end after the `println!` where they are last used, which is before the mutable reference `r3` is created. These scopes don’t overlap, so this code is allowed: the compiler can @@ -246,27 +214,19 @@ compile-time error: Here’s the error: -- ```console {{#include ../listings/ch04-understanding-ownership/no-listing-14-dangling-reference/output.txt}} ``` - - This error message refers to a feature we haven’t covered yet: lifetimes. We’ll discuss lifetimes in detail in Chapter 10. But, if you disregard the parts about lifetimes, the message does contain the key to why this code is a problem: -- ```text this function's return type contains a borrowed value, but there is no value for it to be borrowed from ``` - - Let’s take a closer look at exactly what’s happening at each stage of our `dangle` code: @@ -285,14 +245,10 @@ won’t let us do this. The solution here is to return the `String` directly: -- ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-16-no-dangle/src/main.rs:here}} ``` - - This works without any problems. Ownership is moved out, and nothing is deallocated. diff --git a/src/ch04-03-slices.md b/src/ch04-03-slices.md index 9948b14862..69fb35f82d 100644 --- a/src/ch04-03-slices.md +++ b/src/ch04-03-slices.md @@ -12,14 +12,10 @@ one word, so the entire string should be returned. Let’s work through how we’d write the signature of this function without using slices, to understand the problem that slices will solve: -- ```rust,ignore fn first_word(s: &String) -> ? ``` - - The `first_word` function has a `&String` as a parameter. We don’t want ownership, so this is fine. But what should we return? We don’t really have a way to talk about *part* of a string. However, we could return the index of the @@ -37,24 +33,16 @@ Because we need to go through the `String` element by element and check whether a value is a space, we’ll convert our `String` to an array of bytes using the `as_bytes` method. -- ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:as_bytes}} ``` - - Next, we create an iterator over the array of bytes using the `iter` method: -- ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:iter}} ``` - - We’ll discuss iterators in more detail in [Chapter 13][ch13]. For now, know that `iter` is a method that returns each element in a collection and that `enumerate` wraps the result of `iter` and returns each element as @@ -73,14 +61,10 @@ Inside the `for` loop, we search for the byte that represents the space by using the byte literal syntax. If we find a space, we return the position. Otherwise, we return the length of the string by using `s.len()`. -- ```rust,ignore {{#rustdoc_include ../listings/ch04-understanding-ownership/listing-04-07/src/main.rs:inside_for}} ``` - - We now have a way to find out the index of the end of the first word in the string, but there’s a problem. We’re returning a `usize` on its own, but it’s only a meaningful number in the context of the `&String`. In other words, @@ -106,14 +90,10 @@ Having to worry about the index in `word` getting out of sync with the data in `s` is tedious and error prone! Managing these indices is even more brittle if we write a `second_word` function. Its signature would have to look like this: -- ```rust,ignore fn second_word(s: &String) -> (usize, usize) { ``` - - Now we’re tracking a starting *and* an ending index, and we have even more values that were calculated from data in a particular state but aren’t tied to that state at all. We have three unrelated variables floating around that need @@ -125,14 +105,10 @@ Luckily, Rust has a solution to this problem: string slices. A *string slice* is a reference to part of a `String`, and it looks like this: -- ```rust {{#rustdoc_include ../listings/ch04-understanding-ownership/no-listing-17-slice/src/main.rs:here}} ``` - - Rather than a reference to the entire `String`, `hello` is a reference to a portion of the `String`, specified in the extra `[0..5]` bit. We create slices using a range within brackets by specifying `[starting_index..ending_index]`, @@ -157,8 +133,6 @@ src="img/trpl04-06.svg" class="center" style="width: 50%;" /> With Rust’s `..` range syntax, if you want to start at index 0, you can drop the value before the two periods. In other words, these are equal: -- ```rust let s = String::from("hello"); @@ -166,13 +140,9 @@ let slice = &s[0..2]; let slice = &s[..2]; ``` - - By the same token, if your slice includes the last byte of the `String`, you can drop the trailing number. That means these are equal: -- ```rust let s = String::from("hello"); @@ -182,13 +152,9 @@ let slice = &s[3..len]; let slice = &s[3..]; ``` - - You can also drop both values to take a slice of the entire string. So these are equal: -- ```rust let s = String::from("hello"); @@ -198,8 +164,6 @@ let slice = &s[0..len]; let slice = &s[..]; ``` - - > Note: String slice range indices must occur at valid UTF-8 character > boundaries. If you attempt to create a string slice in the middle of a > multibyte character, your program will exit with an error. For the purposes @@ -229,14 +193,10 @@ the slice and the number of elements in the slice. Returning a slice would also work for a `second_word` function: -- ```rust,ignore fn second_word(s: &String) -> &str { ``` - - We now have a straightforward API that’s much harder to mess up because the compiler will ensure the references into the `String` remain valid. Remember the bug in the program in Listing 4-8, when we got the index to the end of the @@ -257,14 +217,10 @@ compile-time error: Here’s the compiler error: -- ```console {{#include ../listings/ch04-understanding-ownership/no-listing-19-slice-error/output.txt}} ``` - - Recall from the borrowing rules that if we have an immutable reference to something, we cannot also take a mutable reference. Because `clear` needs to truncate the `String`, it needs to get a mutable reference. The `println!` @@ -282,14 +238,10 @@ but it has also eliminated an entire class of errors at compile time! Recall that we talked about string literals being stored inside the binary. Now that we know about slices, we can properly understand string literals: -- ```rust let s = "Hello, world!"; ``` - - The type of `s` here is `&str`: it’s a slice pointing to that specific point of the binary. This is also why string literals are immutable; `&str` is an immutable reference. @@ -299,14 +251,10 @@ immutable reference. Knowing that you can take slices of literals and `String` values leads us to one more improvement on `first_word`, and that’s its signature: -- ```rust,ignore fn first_word(s: &String) -> &str { ``` - - A more experienced Rustacean would write the signature shown in Listing 4-9 instead because it allows us to use the same function on both `&String` values and `&str` values. @@ -341,19 +289,13 @@ makes our API more general and useful without losing any functionality: String slices, as you might imagine, are specific to strings. But there’s a more general slice type too. Consider this array: -- ```rust let a = [1, 2, 3, 4, 5]; ``` - - Just as we might want to refer to part of a string, we might want to refer to part of an array. We’d do so like this: -- ```rust let a = [1, 2, 3, 4, 5]; @@ -362,8 +304,6 @@ let slice = &a[1..3]; assert_eq!(slice, &[2, 3]); ``` - - This slice has the type `&[i32]`. It works the same way as string slices do, by storing a reference to the first element and a length. You’ll use this kind of slice for all sorts of other collections. We’ll discuss these collections in