diff --git a/src/ch13-01-closures.md b/src/ch13-01-closures.md index 2f2b7f9..be9f8a6 100644 --- a/src/ch13-01-closures.md +++ b/src/ch13-01-closures.md @@ -202,235 +202,10 @@ closure form instead The compiler even reminds us that this only works with closures! -### Closures, Ownership, and Borrowing - -The property of being allowed to use variables from the surrounding scope is -also subject to all of the usual rules around ownership and borrowing. Since -closures attempt to infer the types of their parameters, they also infer how -those parameters are borrowed. Closures make that inference by looking at how -they are used. Consider the example in Listing 13-5 that has functions that -borrow immutably, borrow mutably, and move their parameters, then closures that -reference values from their environment and call each of the functions. We'll -see how this affects inference of when a value is borrowed: - -
-Filename: src/main.rs - -```rust -#[derive(Debug)] -struct Foo; - -fn borrows(f: &Foo) { - println!("Took {:?} by reference.", f); -} - -fn borrows_mut(f: &mut Foo) { - println!("Took {:?} by mutable reference.", f); -} - -fn moves(f: Foo) { - println!("Took ownership of {:?}.", f); -} - -fn main() { - let f1 = Foo; - let closure_that_borrows = |x| borrows(x); - closure_that_borrows(&f1); - - let mut f2 = Foo; - let closure_that_borrows_mut = |y| borrows_mut(y); - closure_that_borrows_mut(&mut f2); - - let f3 = Foo; - let closure_that_moves = |z| moves(z); - closure_that_moves(f3); -} -``` - -
- -Listing 13-5: Closures that borrow, borrow mutably, and take ownership of their -parameters, which is inferred from how the closure body uses the parameters - -
-
- -Here, Rust is able to look at how we use the parameters of each closure inside -their bodies. If the closure passes its parameter it to a function that takes -`&Foo`, then the type of the parameter must be `&Foo`. If it passes the -parameter to a function that takes `&mut Foo`, then the type of parameter must -be `&mut Foo`, and so on. If we try to use `f3` after the call to -`closure_that_moves` in the last line of `main`, we'll get a compiler error -since ownership of `f3` was transferred to `closure_that_moves`, which -transferred ownership to the function `moves`. - -### Overriding Inferred Borrowing with the `move` Keyword - -Rust will allow you to override the borrowing inference by using the `move` -keyword. This will cause all of the closure's parameters to be taken by -ownership, instead of whatever they were inferred as. Consider this example: - -```rust -let mut num = 4; - -{ - let mut add_num = |x| num += x; - - add_num(6); -} - -assert_eq!(10, num); -``` - -In this case, the `add_num` closure took a mutable reference to `num`, then -when we called `add_num`, it mutated the underlying value. In the last line, -`num` contains 10, as we'd expect. We also needed to declare `add_num` itself -as `mut` too, because we're mutating its environment. - -If we change the definition of `add_num` to a `move` closure, the behavior is -different: - -```rust -let mut num = 4; - -{ - let mut add_num = move |x| num += x; - - add_num(6); -} - -assert_eq!(4, num); -``` - -In the last line, `num` now contains 4: `add_num` took ownership of a copy of -`num`, rather than mutably borrowing `num`. - -One of the most common places you'll see the `move` keyword used is with -threads, since it's important that one thread is no longer allowed to use a -value once the value has been transferred to another thread through a closure -in order to prevent data races. We'll talk more about that in Chapter XX. - -### Closures and Lifetimes - -Remember Listing 10-8 from the Lifetime Syntax section of Chapter 10? It looked -like this: - -```rust,ignore -{ - let r; - - { - let x = 5; - r = &x; - } - - println!("r: {}", r); -} -``` - -This example doesn't compile since `x` doesn't have a long enough lifetime. -Because closures may borrow variables from their enclosing scope, we can -construct a similar example with a closure that borrows `x` and tries to return -that borrowed value. The code in Listing 13-6 also won't compile: - -
- -```rust,ignore -{ - let closure; - - { - let x = 4; - - closure = || x ; // A closure that takes no arguments and returns x. - } -} -``` - -
- -Listing 13-6: A closure that tries to return a borrowed value that does not live -long enough - -
-
- -We get an error because `x` does not live long enough: - -```text -error: `x` does not live long enough - --> - | -8 | closure = || x ; // A closure that takes no arguments and returns x. - | -- ^ does not live long enough - | | - | capture occurs here -9 | } - | - borrowed value only lives until here -10 | } - | - borrowed value needs to live until here -``` - -To fix the error in the code in Listing 13-6, we can use the `move` keyword -from the last section to make the closure take ownership of `x`. Because `x` is -a number, it is a `Copy` type and therefore will be copied into the closure. -The code in Listing 13-7 will compile: - -
- -```rust -{ - let closure; - - { - let mut x = 4; - - closure = move || x ; // A closure that takes no arguments and returns x. - - x = 5; - - assert_eq!(closure(), 4); - } -} -``` - -
- -Listing 13-7: Moving a value into the closure to fix the lifetime error - -
-
- -Even though we modified `x` between the closure definition and `assert_eq!`, -since `closure` now has its own version, the changes to `x` won't change the -version of `x` that's in the closure. - -Rust doesn't provide a way to say that some values a closure uses should be -borrowed and some should be moved; it's either all by inference or all moved by -adding the `move` keyword. However, we can accomplish the goal of borrowing -some values and taking ownership of others by combining `move` with some extra -bindings. Consider this example where we want to borrow `s1` but take ownership -of `s2`: - -```rust -let s1 = String::from("hello"); -let s2 = String::from("goodbye"); - -let r = &s1; - -let calculation = move || { - r; - s2; -}; - -println!("Can still use s1 here but not s2: {}", s1); -``` - -We've declared `calculation` to `move` all the values it references. Before -defining `calculation`, we declare a new variable `r` that borrows `s1`. Then -in the body of the `calculation` closure, we use `r` instead of using `s1` -directly. The closure takes ownership of `r`, but `r` is a reference, so the -closure hasn't taken ownership of `s1` even though `calculation` uses `move`. +Creating closures that capture values from their environment is mostly used in +the context of starting new threads. We'll show some more examples and explain +more detail about this feature of closures in Chapter 16 when we talk about +concurrency. ### Closures as Function Parameters Using the `Fn` Traits diff --git a/src/ch13-02-iterators.md b/src/ch13-02-iterators.md index 35a6655..b7e4c03 100644 --- a/src/ch13-02-iterators.md +++ b/src/ch13-02-iterators.md @@ -1,7 +1,7 @@ ## Iterators Iterators are a pattern in Rust that allows you to do some processing on a -sequence of items. For example, the code in Listing 13-8 adds one to each +sequence of items. For example, the code in Listing 13-5 adds one to each number in a vector:
@@ -16,7 +16,7 @@ assert_eq!(v2, [2, 3, 4]);
-Listing 13-8: Using an iterator, `map`, and `collect` to add one to each number +Listing 13-5: Using an iterator, `map`, and `collect` to add one to each number in a vector
@@ -53,7 +53,7 @@ behavior of an iterator adaptor like `map`. In the previous section, you may have noticed a subtle difference in wording: we said that `map` *adapts* an iterator, but `collect` *consumes* one. That was intentional. By themselves, iterators won't do anything; they're lazy. That is, -if we write code like Listing 13-8 except we don't call `collect`: +if we write code like Listing 13-5 except we don't call `collect`: ```rust let v1: Vec = vec![1, 2, 3]; @@ -125,7 +125,7 @@ defining the body of the `next` method. The way we want our iterator to work is to add one to the state (which is why we initialized `count` to 0, since we want our iterator to return one first). If `count` is still less than six, we'll return the current value, but if `count` is six or higher, our iterator will -return `None`: +return `None`, as shown in Listing 13-6:
@@ -154,7 +154,7 @@ impl Iterator for Counter {
-Listing 13-9: Implementing the `Iterator` trait on our `Counter` struct +Listing 13-6: Implementing the `Iterator` trait on our `Counter` struct
@@ -218,8 +218,8 @@ line. ### All Sorts of `Iterator` Adaptors -In Listing 13-8, we had iterators and we called methods like `map` and -`collect` on them. In Listing 13-9, however, we only implemented the `next` +In Listing 13-5, we had iterators and we called methods like `map` and +`collect` on them. In Listing 13-6, however, we only implemented the `next` method on our `Counter`. How do we get methods like `map` and `collect` on our `Counter`? diff --git a/src/ch16-00-concurrency.md b/src/ch16-00-concurrency.md index c9603ed..9db08e0 100644 --- a/src/ch16-00-concurrency.md +++ b/src/ch16-00-concurrency.md @@ -41,6 +41,238 @@ Code examples - just print stuff, no data sharing ## Communicating between threads + +### Closures, Ownership, and Borrowing + +The property of being allowed to use variables from the surrounding scope is +also subject to all of the usual rules around ownership and borrowing. Since +closures attempt to infer the types of their parameters, they also infer how +those parameters are borrowed. Closures make that inference by looking at how +they are used. Consider the example in Listing 13-5 that has functions that +borrow immutably, borrow mutably, and move their parameters, then closures that +reference values from their environment and call each of the functions. We'll +see how this affects inference of when a value is borrowed: + +
+Filename: src/main.rs + +```rust +#[derive(Debug)] +struct Foo; + +fn borrows(f: &Foo) { + println!("Took {:?} by reference.", f); +} + +fn borrows_mut(f: &mut Foo) { + println!("Took {:?} by mutable reference.", f); +} + +fn moves(f: Foo) { + println!("Took ownership of {:?}.", f); +} + +fn main() { + let f1 = Foo; + let closure_that_borrows = |x| borrows(x); + closure_that_borrows(&f1); + + let mut f2 = Foo; + let closure_that_borrows_mut = |y| borrows_mut(y); + closure_that_borrows_mut(&mut f2); + + let f3 = Foo; + let closure_that_moves = |z| moves(z); + closure_that_moves(f3); +} +``` + +
+ +Listing 16-something: Closures that borrow, borrow mutably, and take ownership +of their parameters, which is inferred from how the closure body uses the +parameters + +
+
+ +Here, Rust is able to look at how we use the parameters of each closure inside +their bodies. If the closure passes its parameter it to a function that takes +`&Foo`, then the type of the parameter must be `&Foo`. If it passes the +parameter to a function that takes `&mut Foo`, then the type of parameter must +be `&mut Foo`, and so on. If we try to use `f3` after the call to +`closure_that_moves` in the last line of `main`, we'll get a compiler error +since ownership of `f3` was transferred to `closure_that_moves`, which +transferred ownership to the function `moves`. + +### Overriding Inferred Borrowing with the `move` Keyword + +Rust will allow you to override the borrowing inference by using the `move` +keyword. This will cause all of the closure's parameters to be taken by +ownership, instead of whatever they were inferred as. Consider this example: + +```rust +let mut num = 4; + +{ + let mut add_num = |x| num += x; + + add_num(6); +} + +assert_eq!(10, num); +``` + +In this case, the `add_num` closure took a mutable reference to `num`, then +when we called `add_num`, it mutated the underlying value. In the last line, +`num` contains 10, as we'd expect. We also needed to declare `add_num` itself +as `mut` too, because we're mutating its environment. + +If we change the definition of `add_num` to a `move` closure, the behavior is +different: + +```rust +let mut num = 4; + +{ + let mut add_num = move |x| num += x; + + add_num(6); +} + +assert_eq!(4, num); +``` + +In the last line, `num` now contains 4: `add_num` took ownership of a copy of +`num`, rather than mutably borrowing `num`. + +One of the most common places you'll see the `move` keyword used is with +threads, since it's important that one thread is no longer allowed to use a +value once the value has been transferred to another thread through a closure +in order to prevent data races. We'll talk more about that in Chapter XX. + +### Closures and Lifetimes + +Remember Listing 10-8 from the Lifetime Syntax section of Chapter 10? It looked +like this: + +```rust,ignore +{ + let r; + + { + let x = 5; + r = &x; + } + + println!("r: {}", r); +} +``` + +This example doesn't compile since `x` doesn't have a long enough lifetime. +Because closures may borrow variables from their enclosing scope, we can +construct a similar example with a closure that borrows `x` and tries to return +that borrowed value. The code in Listing 13-6 also won't compile: + +
+ +```rust,ignore +{ + let closure; + + { + let x = 4; + + closure = || x ; // A closure that takes no arguments and returns x. + } +} +``` + +
+ +Listing 16-something: A closure that tries to return a borrowed value that does +not live long enough + +
+
+ +We get an error because `x` does not live long enough: + +```text +error: `x` does not live long enough + --> + | +8 | closure = || x ; // A closure that takes no arguments and returns x. + | -- ^ does not live long enough + | | + | capture occurs here +9 | } + | - borrowed value only lives until here +10 | } + | - borrowed value needs to live until here +``` + +To fix the error in the code in Listing 13-6, we can use the `move` keyword +from the last section to make the closure take ownership of `x`. Because `x` is +a number, it is a `Copy` type and therefore will be copied into the closure. +The code in Listing 13-7 will compile: + +
+ +```rust +{ + let closure; + + { + let mut x = 4; + + closure = move || x ; // A closure that takes no arguments and returns x. + + x = 5; + + assert_eq!(closure(), 4); + } +} +``` + +
+ +Listing 16-something: Moving a value into the closure to fix the lifetime error + +
+
+ +Even though we modified `x` between the closure definition and `assert_eq!`, +since `closure` now has its own version, the changes to `x` won't change the +version of `x` that's in the closure. + +Rust doesn't provide a way to say that some values a closure uses should be +borrowed and some should be moved; it's either all by inference or all moved by +adding the `move` keyword. However, we can accomplish the goal of borrowing +some values and taking ownership of others by combining `move` with some extra +bindings. Consider this example where we want to borrow `s1` but take ownership +of `s2`: + +```rust +let s1 = String::from("hello"); +let s2 = String::from("goodbye"); + +let r = &s1; + +let calculation = move || { + r; + s2; +}; + +println!("Can still use s1 here but not s2: {}", s1); +``` + +We've declared `calculation` to `move` all the values it references. Before +defining `calculation`, we declare a new variable `r` that borrows `s1`. Then +in the body of the `calculation` closure, we use `r` instead of using `s1` +directly. The closure takes ownership of `r`, but `r` is a reference, so the +closure hasn't taken ownership of `s1` even though `calculation` uses `move`. + ### `Channels` Look up examples of cases where channels are useful