From af952739f9c6f2754339503e17b2634fe931adf6 Mon Sep 17 00:00:00 2001 From: Adam Dunlap Date: Sun, 1 Jan 2017 15:51:43 -0800 Subject: [PATCH 01/18] Adding information on closures and iterators Not well edited and doesn't have everything about iterators --- src/ch13-00-functional-features.md | 130 ++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 3 deletions(-) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 7366abfce9..484ac52d5b 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -4,19 +4,143 @@ ### What is a closure -How are they diff from fns +In programming languages, a closure is a lot like a function. Like a function, closures contain code +that is executed when the closure is called. The main difference, besides syntax, from functions is +that closures have *capture*. What this means is that closures can use variables in its surrounding +scope. Consider the following code: + +```rust,ignore +fn main() { + let x = 4; + fn is_x(z: i32) -> bool { + z == x + } + + let y = 4; + is_x(y); +} +``` + +Here, the function `is_x` is trying to use the x from `main`'s scope. This doesn't work, however, +giving the error + +```text +error: can't capture dynamic environment in a fn item; use the || { ... } closure form instead [E0434] +z == x + ^ +``` + +This error message is saying that functions can't *capture* -- only closures can. So, let's try +making the `is_x` function into a closure: +```rust +fn main() { + let x = 4; + let is_x = |z: i32| -> bool { + z == x + }; + + let y = 4; + is_x(y); +} +``` + +We can see here that the syntax for defining a closure is `|input arguments| -> output { code }`. +This is very similar to a function definition with some major differences. The name is not a part of +the closure -- by default, closures are unnamed. We also use vertical bars (`|`) instead of +parentheses to define the arguments to the closure, and specifying the types of the inputs and +outputs is optional if Rust can figure them out. Finally, closures are expressions rather than +statements. You can see here that we could assign the closure to a variable, and needed to terminate +the line with a semicolon. + +This closure is capturing the x variable. But how is it doing that? What if we define a closure in a +function and return it?What if we change x between where the closure is defined and where it's +executed? + +By default, closures capture the variables by reference. This means that closures cannot outlive +variables that they capture. If we try to compile the following code: +```rust,ignore +fn main() { + let closure; + { + let x = 4; + closure = ||{ x }; // Closure that takes no arguments and returns x + } +} +``` +we get an error because `x` does not live long enough. + +We can make closures move (or copy, for types declared Copy) their values in by using the `move` +keyword: +```rust +fn main() { + let closure; + { + let x = 4; + closure = move ||{ x }; // Closure that takes no arguments and returns x + } +} +``` ### `Fn` traits +It's clear that closurse more than functions, because closures have to keep track of what variables +they've captured. Closures are essentially functions that come with a struct that includes their +captured variables (or references). This means that every closure is a different type. If we want a +function to take a closure as an argument, we can't simply talk about a "closure" type, because each +one is a different type. The way that Rust manages this is with traits. There are three traits that +can be automatically applied to each closure, depending on what the closure is doing: `FnOnce`, +`FnMut`, and `Fn`. These traits derive from each other, so if a type is `FnMut`, it must also be +`FnOnce`, and if a type is `Fn`, it must be both `FnMut` and `FnOnce`. Closures automatically derive +from the ones that are appropriate, and you cannot currently derive them for your custom types. + +If you want to write a function such as `map` that takes in a function, you should almost always +take in one of the `Fn` traits. The `FnOnce` trait defines a function `call_once` that consumes +`self`. It's the most general option, and if your function will only call the given function once, +it should take in an `FnOnce`. `FnMut` is the next most general, since its `call_mut` function takes +`self` by mutable reference, so you need a mutable reference to the closure to call it. `Fn` is the +most specific of these, but you only need a immutable reference to call a function that is `Fn`. + +All functions and closures implement `FnOnce`. If a closure takes its variables by reference rather +than by move, then it also implements `FnMut`. If the closure modifies none of its captures, then it +also implements `Fn`. All functions also implement `Fn` because they don't capture at all. + ## Iterators +Iterators are types that implement the `Iterator` trait. Iterators are designed to return a sequence +of values by repetedly calling their `next()` method. Often, these values come from a data structure +such as `Vec`. Iterators are powerful, however, because they can be used for more than that. For +example, you can have an infinite iterator -- one whose `next()` method never returns `None`. There +are also functions on iterators like `map`, which applies a function to each value in the iterators +as they're requested. `map` has low memory usage because it only applies the function as elements +are requested, so the whole sequence does not need to build up. + ### Iterator & for loop -.into_iter() +`for` loops are usually said to "iterate" over things, so it makes perfect sense that in Rust, for +loops use iterators. Specifically, you can say `for x in y` if `y` implements the `IntoIterator` +trait with the `into_iter` method. This method consumes `self` and (for data structures) returns an +iterator that returns the values in the data structure. + +Common `IntoIterator` types are data structures and ranges such as `0..10`. Thus, when you say `for +i in 0..10`, you are creating a `Range` that knows its start and end, then calling `into_iter` on it +to convert it to an iterator, and then repeatedly calling `next()` on that iterator to get the +numbers 0, 1, ..., 9. ### Iterators are Lazy -Difference between adapter and consumer - another iterator or consuming? +An important and very useful fact about iterators is that they are _lazy_. This means that, in +general, iterators don't look at the things they're iterating over until they are about to return +it. This is very useful when your iterator will return a large (or perhaps even infinite!) sequence. +Another consequence of this is that you must be careful when using the `map` function with a closure +that mutates things. The closure will not be executed on any elements until the resulting iterator +is consumed. + +Iterator adapters are iterators that are based off of other iterators. A simple iterator adaptor in +the standard library is the [take](https://doc.rust-lang.org/std/iter/struct.Take.html) adaptor. +This iterator adaptor contains a counter initialized with a user-defined number and another +iterator. When its `next()` method is called, it decrements the count, and if it's 0, it returns +`None`. Otherwise, it returns `next()` of its contained iterator. In this way, we _adapt_ the inner +iterator to only return the first `n` elements rather than everything. ### Implementing the Iterator trait From 5b2bf6da8344602f70ec9ebaed7391f9b14e0135 Mon Sep 17 00:00:00 2001 From: Alexander Dunlap Date: Sun, 1 Jan 2017 16:01:52 -0800 Subject: [PATCH 02/18] Minor grammar fix --- src/ch13-00-functional-features.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 484ac52d5b..e2db689e68 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -5,8 +5,8 @@ ### What is a closure In programming languages, a closure is a lot like a function. Like a function, closures contain code -that is executed when the closure is called. The main difference, besides syntax, from functions is -that closures have *capture*. What this means is that closures can use variables in its surrounding +that is executed when the closure is called. The main difference, besides syntax, between closures and functions is +that closures have *capture*. What this means is that a closure can use variables in its surrounding scope. Consider the following code: ```rust,ignore From f1d7eea822a2a60e79e428148c41581280dee5b9 Mon Sep 17 00:00:00 2001 From: Adam Dunlap Date: Sun, 1 Jan 2017 16:03:48 -0800 Subject: [PATCH 03/18] Updating text wrapping --- src/ch13-00-functional-features.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index e2db689e68..992d241a60 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -5,9 +5,9 @@ ### What is a closure In programming languages, a closure is a lot like a function. Like a function, closures contain code -that is executed when the closure is called. The main difference, besides syntax, between closures and functions is -that closures have *capture*. What this means is that a closure can use variables in its surrounding -scope. Consider the following code: +that is executed when the closure is called. The main difference, besides syntax, between closures +and functions is that closures have *capture*. What this means is that a closure can use variables +in its surrounding scope. Consider the following code: ```rust,ignore fn main() { From 0cdc0dd1925e761ea106ce0da927733b7f135145 Mon Sep 17 00:00:00 2001 From: Adam Dunlap Date: Sun, 1 Jan 2017 16:07:22 -0800 Subject: [PATCH 04/18] Clarifications/typos --- src/ch13-00-functional-features.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 992d241a60..28815019d7 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -50,10 +50,11 @@ the closure -- by default, closures are unnamed. We also use vertical bars (`|`) parentheses to define the arguments to the closure, and specifying the types of the inputs and outputs is optional if Rust can figure them out. Finally, closures are expressions rather than statements. You can see here that we could assign the closure to a variable, and needed to terminate -the line with a semicolon. +the line with a semicolon. Frequently, closures are defined and passed into functions without ever +giving them names. This closure is capturing the x variable. But how is it doing that? What if we define a closure in a -function and return it?What if we change x between where the closure is defined and where it's +function and return it? What if we change x between where the closure is defined and where it's executed? By default, closures capture the variables by reference. This means that closures cannot outlive From eaa6b80dae2e12a86c90568f5279e3017b6d4866 Mon Sep 17 00:00:00 2001 From: Adam Dunlap Date: Sun, 1 Jan 2017 21:49:51 -0800 Subject: [PATCH 05/18] Fix spelling errors --- src/ch13-00-functional-features.md | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 28815019d7..8ae8dd0e9c 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -84,15 +84,16 @@ fn main() { ### `Fn` traits -It's clear that closurse more than functions, because closures have to keep track of what variables -they've captured. Closures are essentially functions that come with a struct that includes their -captured variables (or references). This means that every closure is a different type. If we want a -function to take a closure as an argument, we can't simply talk about a "closure" type, because each -one is a different type. The way that Rust manages this is with traits. There are three traits that -can be automatically applied to each closure, depending on what the closure is doing: `FnOnce`, -`FnMut`, and `Fn`. These traits derive from each other, so if a type is `FnMut`, it must also be -`FnOnce`, and if a type is `Fn`, it must be both `FnMut` and `FnOnce`. Closures automatically derive -from the ones that are appropriate, and you cannot currently derive them for your custom types. +It's clear that closures are more than functions, because closures have to keep track of what +variables they've captured. Closures are essentially functions that come with a struct that includes +their captured variables (or references). This means that every closure is a different type. If we +want a function to take a closure as an argument, we can't simply talk about a "closure" type, +because each one is a different type. The way that Rust manages this is with traits. There are three +traits that can be automatically applied to each closure, depending on what the closure is doing: +`FnOnce`, `FnMut`, and `Fn`. These traits derive from each other, so if a type is `FnMut`, it must +also be `FnOnce`, and if a type is `Fn`, it must be both `FnMut` and `FnOnce`. Closures +automatically derive from the ones that are appropriate, and you cannot currently derive them for +your custom types. If you want to write a function such as `map` that takes in a function, you should almost always take in one of the `Fn` traits. The `FnOnce` trait defines a function `call_once` that consumes @@ -108,12 +109,12 @@ also implements `Fn`. All functions also implement `Fn` because they don't captu ## Iterators Iterators are types that implement the `Iterator` trait. Iterators are designed to return a sequence -of values by repetedly calling their `next()` method. Often, these values come from a data structure -such as `Vec`. Iterators are powerful, however, because they can be used for more than that. For -example, you can have an infinite iterator -- one whose `next()` method never returns `None`. There -are also functions on iterators like `map`, which applies a function to each value in the iterators -as they're requested. `map` has low memory usage because it only applies the function as elements -are requested, so the whole sequence does not need to build up. +of values by repeatedly calling their `next()` method. Often, these values come from a data +structure such as `Vec`. Iterators are powerful, however, because they can be used for more than +that. For example, you can have an infinite iterator -- one whose `next()` method never returns +`None`. There are also functions on iterators like `map`, which applies a function to each value in +the iterators as they're requested. `map` has low memory usage because it only applies the function +as elements are requested, so the whole sequence does not need to build up. ### Iterator & for loop @@ -136,9 +137,9 @@ Another consequence of this is that you must be careful when using the `map` fun that mutates things. The closure will not be executed on any elements until the resulting iterator is consumed. -Iterator adapters are iterators that are based off of other iterators. A simple iterator adaptor in -the standard library is the [take](https://doc.rust-lang.org/std/iter/struct.Take.html) adaptor. -This iterator adaptor contains a counter initialized with a user-defined number and another +Iterator adapters are iterators that are based off of other iterators. A simple iterator adapter in +the standard library is the [take](https://doc.rust-lang.org/std/iter/struct.Take.html) adapter. +This iterator contains a counter initialized with a user-defined number and another iterator. When its `next()` method is called, it decrements the count, and if it's 0, it returns `None`. Otherwise, it returns `next()` of its contained iterator. In this way, we _adapt_ the inner iterator to only return the first `n` elements rather than everything. @@ -161,4 +162,3 @@ Most complicated chain of iterator functions that compile down to the same ASM a ### Representation: Closures are a Struct Closures don't have any further performance penalty over regular fn calls - From fcf95cdcf83f0c62247a98030bd1d9950e9426a6 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 10 Jan 2017 10:51:26 -0500 Subject: [PATCH 06/18] word wrap and tweak a bit --- src/ch13-00-functional-features.md | 170 ++++++++++++++++------------- 1 file changed, 95 insertions(+), 75 deletions(-) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 8ae8dd0e9c..4c75b4579a 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -4,10 +4,11 @@ ### What is a closure -In programming languages, a closure is a lot like a function. Like a function, closures contain code -that is executed when the closure is called. The main difference, besides syntax, between closures -and functions is that closures have *capture*. What this means is that a closure can use variables -in its surrounding scope. Consider the following code: +In programming languages, a closure is a lot like a function. Like a function, +closures contain code that is executed when the closure is called. The main +difference, besides syntax, between closures and functions is that closures +have *capture*. What this means is that a closure can use variables in its +surrounding scope. Consider the following code: ```rust,ignore fn main() { @@ -21,8 +22,8 @@ fn main() { } ``` -Here, the function `is_x` is trying to use the x from `main`'s scope. This doesn't work, however, -giving the error +Here, the function `is_x` is trying to use the x from `main`'s scope. This +doesn't work, however, giving the error ```text error: can't capture dynamic environment in a fn item; use the || { ... } closure form instead [E0434] @@ -30,8 +31,9 @@ z == x ^ ``` -This error message is saying that functions can't *capture* -- only closures can. So, let's try -making the `is_x` function into a closure: +This error message is saying that functions can't *capture* -- only closures +can. So, let's try making the `is_x` function into a closure: + ```rust fn main() { let x = 4; @@ -44,21 +46,25 @@ fn main() { } ``` -We can see here that the syntax for defining a closure is `|input arguments| -> output { code }`. -This is very similar to a function definition with some major differences. The name is not a part of -the closure -- by default, closures are unnamed. We also use vertical bars (`|`) instead of -parentheses to define the arguments to the closure, and specifying the types of the inputs and -outputs is optional if Rust can figure them out. Finally, closures are expressions rather than -statements. You can see here that we could assign the closure to a variable, and needed to terminate -the line with a semicolon. Frequently, closures are defined and passed into functions without ever -giving them names. - -This closure is capturing the x variable. But how is it doing that? What if we define a closure in a -function and return it? What if we change x between where the closure is defined and where it's -executed? - -By default, closures capture the variables by reference. This means that closures cannot outlive -variables that they capture. If we try to compile the following code: +We can see here that the syntax for defining a closure is `|input arguments| -> +output { code }`. This is very similar to a function definition with some +major differences. The name is not a part of the closure -- by default, +closures are unnamed. We also use vertical bars (`|`) instead of parentheses to +define the arguments to the closure, and specifying the types of the inputs and +outputs is optional if Rust can figure them out. Finally, closures are +expressions rather than statements. You can see here that we could assign the +closure to a variable, and needed to terminate the line with a semicolon. +Frequently, closures are defined and passed into functions without ever giving +them names. + +This closure is capturing the x variable. But how is it doing that? What if we +define a closure in a function and return it? What if we change x between where +the closure is defined and where it's executed? + +By default, closures capture the variables by reference. This means that +closures cannot outlive variables that they capture. If we try to compile the +following code: + ```rust,ignore fn main() { let closure; @@ -68,10 +74,12 @@ fn main() { } } ``` -we get an error because `x` does not live long enough. -We can make closures move (or copy, for types declared Copy) their values in by using the `move` -keyword: +We get an error because `x` does not live long enough. + +We can make closures move (or copy, for types declared Copy) their values in by +using the `move` keyword: + ```rust fn main() { let closure; @@ -84,65 +92,77 @@ fn main() { ### `Fn` traits -It's clear that closures are more than functions, because closures have to keep track of what -variables they've captured. Closures are essentially functions that come with a struct that includes -their captured variables (or references). This means that every closure is a different type. If we -want a function to take a closure as an argument, we can't simply talk about a "closure" type, -because each one is a different type. The way that Rust manages this is with traits. There are three -traits that can be automatically applied to each closure, depending on what the closure is doing: -`FnOnce`, `FnMut`, and `Fn`. These traits derive from each other, so if a type is `FnMut`, it must -also be `FnOnce`, and if a type is `Fn`, it must be both `FnMut` and `FnOnce`. Closures -automatically derive from the ones that are appropriate, and you cannot currently derive them for -your custom types. - -If you want to write a function such as `map` that takes in a function, you should almost always -take in one of the `Fn` traits. The `FnOnce` trait defines a function `call_once` that consumes -`self`. It's the most general option, and if your function will only call the given function once, -it should take in an `FnOnce`. `FnMut` is the next most general, since its `call_mut` function takes -`self` by mutable reference, so you need a mutable reference to the closure to call it. `Fn` is the -most specific of these, but you only need a immutable reference to call a function that is `Fn`. - -All functions and closures implement `FnOnce`. If a closure takes its variables by reference rather -than by move, then it also implements `FnMut`. If the closure modifies none of its captures, then it -also implements `Fn`. All functions also implement `Fn` because they don't capture at all. +It's clear that closures are more than functions, because closures have to keep +track of what variables they've captured. Closures are essentially functions +that come with a struct that includes their captured variables (or references). +This means that every closure is a different type. If we want a function to +take a closure as an argument, we can't simply talk about a "closure" type, +because each one is a different type. The way that Rust manages this is with +traits. There are three traits that can be automatically applied to each +closure, depending on what the closure is doing: `FnOnce`, `FnMut`, and `Fn`. +These traits derive from each other, so if a type is `FnMut`, it must also be +`FnOnce`, and if a type is `Fn`, it must be both `FnMut` and `FnOnce`. Closures +automatically derive from the ones that are appropriate, and you cannot +currently derive them for your custom types. + +If you want to write a function such as `map` that takes in a function, you +should almost always take in one of the `Fn` traits. The `FnOnce` trait defines +a function `call_once` that consumes `self`. It's the most general option, and +if your function will only call the given function once, it should take in an +`FnOnce`. `FnMut` is the next most general, since its `call_mut` function takes +`self` by mutable reference, so you need a mutable reference to the closure to +call it. `Fn` is the most specific of these, but you only need a immutable +reference to call a function that is `Fn`. + +All functions and closures implement `FnOnce`. If a closure takes its variables +by reference rather than by move, then it also implements `FnMut`. If the +closure modifies none of its captures, then it also implements `Fn`. All +functions also implement `Fn` because they don't capture at all. ## Iterators -Iterators are types that implement the `Iterator` trait. Iterators are designed to return a sequence -of values by repeatedly calling their `next()` method. Often, these values come from a data -structure such as `Vec`. Iterators are powerful, however, because they can be used for more than -that. For example, you can have an infinite iterator -- one whose `next()` method never returns -`None`. There are also functions on iterators like `map`, which applies a function to each value in -the iterators as they're requested. `map` has low memory usage because it only applies the function -as elements are requested, so the whole sequence does not need to build up. +Iterators are types that implement the `Iterator` trait. Iterators are designed +to return a sequence of values by repeatedly calling their `next()` method. +Often, these values come from a data structure such as `Vec`. Iterators are +powerful, however, because they can be used for more than that. For example, +you can have an infinite iterator -- one whose `next()` method never returns +`None`. There are also functions on iterators like `map`, which applies a +function to each value in the iterators as they're requested. `map` has low +memory usage because it only applies the function as elements are requested, so +the whole sequence does not need to build up. ### Iterator & for loop -`for` loops are usually said to "iterate" over things, so it makes perfect sense that in Rust, for -loops use iterators. Specifically, you can say `for x in y` if `y` implements the `IntoIterator` -trait with the `into_iter` method. This method consumes `self` and (for data structures) returns an -iterator that returns the values in the data structure. +`for` loops are usually said to "iterate" over things, so it makes perfect +sense that in Rust, for loops use iterators. Specifically, you can say `for x +in y` if `y` implements the `IntoIterator` trait with the `into_iter` method. +This method consumes `self` and (for data structures) returns an iterator that +returns the values in the data structure. -Common `IntoIterator` types are data structures and ranges such as `0..10`. Thus, when you say `for -i in 0..10`, you are creating a `Range` that knows its start and end, then calling `into_iter` on it -to convert it to an iterator, and then repeatedly calling `next()` on that iterator to get the -numbers 0, 1, ..., 9. +Common `IntoIterator` types are data structures and ranges such as `0..10`. +Thus, when you say `for i in 0..10`, you are creating a `Range` that knows its +start and end, then calling `into_iter` on it to convert it to an iterator, and +then repeatedly calling `next()` on that iterator to get the numbers 0, 1, ..., +9. ### Iterators are Lazy -An important and very useful fact about iterators is that they are _lazy_. This means that, in -general, iterators don't look at the things they're iterating over until they are about to return -it. This is very useful when your iterator will return a large (or perhaps even infinite!) sequence. -Another consequence of this is that you must be careful when using the `map` function with a closure -that mutates things. The closure will not be executed on any elements until the resulting iterator -is consumed. - -Iterator adapters are iterators that are based off of other iterators. A simple iterator adapter in -the standard library is the [take](https://doc.rust-lang.org/std/iter/struct.Take.html) adapter. -This iterator contains a counter initialized with a user-defined number and another -iterator. When its `next()` method is called, it decrements the count, and if it's 0, it returns -`None`. Otherwise, it returns `next()` of its contained iterator. In this way, we _adapt_ the inner -iterator to only return the first `n` elements rather than everything. +An important and very useful fact about iterators is that they are _lazy_. This +means that, in general, iterators don't look at the things they're iterating +over until they are about to return it. This is very useful when your iterator +will return a large (or perhaps even infinite!) sequence. Another consequence +of this is that you must be careful when using the `map` function with a +closure that mutates things. The closure will not be executed on any elements +until the resulting iterator is consumed. + +Iterator adapters are iterators that are based off of other iterators. A simple +iterator adapter in the standard library is the +[take](https://doc.rust-lang.org/std/iter/struct.Take.html) adapter. This +iterator contains a counter initialized with a user-defined number and another +iterator. When its `next()` method is called, it decrements the count, and if +it's 0, it returns `None`. Otherwise, it returns `next()` of its contained +iterator. In this way, we _adapt_ the inner iterator to only return the first +`n` elements rather than everything. ### Implementing the Iterator trait From e0db5134af89089bc0a701f735c2ef02aae108a8 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Tue, 10 Jan 2017 16:05:22 -0500 Subject: [PATCH 07/18] First draft of the iterators chapter --- src/ch13-00-functional-features.md | 983 +++++++++++++++++++++++++---- 1 file changed, 850 insertions(+), 133 deletions(-) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 4c75b4579a..d7e8a4d72a 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -1,184 +1,901 @@ # Functional Language features in Rust - Iterators and Closures +As a language, Rust takes influence from a lot of places. One of those places +is functional programming. We won't use this chapter to debate what exactly +'functional programming' is, but instead, show off some features of Rust that +are similar to many languages that are referred to as functional. + +More specifically, we're going to cover: + +* Closures, a function-like construct you can store in a binding. +* Iterators, a way of processing series of elements. +* Using these features to improve upon the project from the last chapter. +* A bit about these features' performance. Spoiler alert: they're faster than + you might think! + +This is not a complete list of Rust's influence from the functional style; +pattern matching, enums, and many other features are too. But mastering +closures and iterators will help you write idiomatic, fast Rust code. + ## Closures -### What is a closure +Rust gives you the ability to define *closures*, which are sort of like +functions. Instead of giving you a technical definintion, let's dive into +what clousures look like, syntactically: + +```rust +let add_one = |x| x + 1; + +let five = add_one(4); + +assert_eq!(5, five); +``` + +The first line defines a closure, and binds it to the `add_one` variable. The +arguments to the closure go in between the pipes (`|`). + +This is a simple closure with only one expression as its body, let's see one +that's got a bit more going on: + +```rust +let calculate = |a, b| { + let mut result = a * 2; + + result += b; + + result +}; + +assert_eq!(7, calculate(2, 3)); // 2 * 2 + 3 == 7 +assert_eq!(13, calculate(4, 5)); // 4 * 2 + 5 == 13 +``` + +We can use `{}`s to give a closure a body with more than one expression. + +You'll notice a few things about closures that are a bit different from +functions defined with `fn`. The first is that we did not need to annotate the +types of arguments the closure takes or the values it returns. We can: + +```rust +let plus_one = |x: i32| -> i32 { x + 1 }; + +assert_eq!(2, plus_one(1)); +``` + +But we dont have to. Why is this? Functions are part of an explicit interface +that's exposed to your users, so defining this interface rigidly is helpful. +But closures aren't used like this: they're stored in bindings and called +directly. Being forced to annotate the types would be a significant ergonomic +loss for little advantage. + +The syntax is similar, but a bit different. Let's compare more directly. We've +added some spaces here to line up the relevant parts: + +```rust +fn plus_one_v1 (x: i32) -> i32 { x + 1 } // a function +let plus_one_v2 = |x: i32| -> i32 { x + 1 }; // the full syntax for a closure +let plus_one_v3 = |x: i32| { x + 1 }; // a closure eliding types +let plus_one_v4 = |x: i32| x + 1 ; // without braces +``` + +Small differences, but they're similar. Why come up with a different syntax +for closures? There's one additional way in which they're different from +functions: they posses an 'environment'. + +## Closures and their environment + +We've learned that functions can only use variables that are in scope, either +by being static or being declared as parameters. But closures can do more. +They can access variables from their enclosing scope. Like this: + + +```rust +fn main() { + let x = 4; + + let equal_to_x = |z| z == x; + + let y = 4; + + assert!(equal_to_x(y)); +} +``` + +Here, even though `x` is not an argument to `equal_to_x`, it's able to +refer to it, as it's a variable defined in the same scope. We couldn't +do this with a `fn`. If we tried... -In programming languages, a closure is a lot like a function. Like a function, -closures contain code that is executed when the closure is called. The main -difference, besides syntax, between closures and functions is that closures -have *capture*. What this means is that a closure can use variables in its -surrounding scope. Consider the following code: ```rust,ignore fn main() { - let x = 4; - fn is_x(z: i32) -> bool { - z == x - } + let x = 4; + + fn equal_to_x(z) { z == x } - let y = 4; - is_x(y); + let y = 4; + + assert!(equal_to_x(y)); } ``` -Here, the function `is_x` is trying to use the x from `main`'s scope. This -doesn't work, however, giving the error +We'd get an error: ```text -error: can't capture dynamic environment in a fn item; use the || { ... } closure form instead [E0434] +error: can't capture dynamic environment in a fn item; use the || { ... } +closure form instead [E0434] z == x ^ ``` -This error message is saying that functions can't *capture* -- only closures -can. So, let's try making the `is_x` function into a closure: +This only works with closures! This property is also subject to all of the +usual rules around ownership and borrowing. Because closures attempt to infer +the types of their arguments, they also have to infer how they're borrowed. +They'll do that from how they are used. Consider this example: ```rust -fn main() { - let x = 4; - let is_x = |z: i32| -> bool { - z == x - }; +struct Foo; + +fn borrow(f: &Foo) { + println!("Took foo by reference."); +} + +fn borrow_mut(f: &mut Foo) { + println!("Took foo by mutable reference."); +} + +fn moves(f: Foo) { + println!("Took foo by ownership."); +} + +let f = Foo; +let borrows = |f| borrow(f); +borrows(&f); + +let mut f = Foo; +let borrows_mut = |f| borrow_mut(f); +borrows_mut(&mut f); + +let f = Foo; +let moves = |f| moves(f); +moves(f); +``` + +Here, Rust is able to look at how we use `f` inside of each closure. If we pass +it to a function that takes `&Foo`, then the type of `f` must be `&Foo`. If we +pass it to a function that takes `&mut Foo`, then the type of `f` must be `Foo`. +And so on. + +### The `move` keyword + +Rust will allow you to override this inference with the `move` keyword. This +will cause all variables to be taken by ownership, instead of whatever they +were inferred as. Consider this: + +```rust +let mut num = 5; + +{ + let mut add_num = |x| num += x; + + add_num(5); +} - let y = 4; - is_x(y); +assert_eq!(10, num); +``` + +So in this case, our closure took a mutable reference to `num`, and then when +we called `add_num`, it mutated the underlying value, as we'd expect. We also +needed to declare `add_num` as `mut` too, because were mutating its +environment. + +If we change to a `move` closure, its different: + +```rust +let mut num = 5; + +{ + let mut add_num = move |x| num += x; + + add_num(5); } + +assert_eq!(5, num); ``` -We can see here that the syntax for defining a closure is `|input arguments| -> -output { code }`. This is very similar to a function definition with some -major differences. The name is not a part of the closure -- by default, -closures are unnamed. We also use vertical bars (`|`) instead of parentheses to -define the arguments to the closure, and specifying the types of the inputs and -outputs is optional if Rust can figure them out. Finally, closures are -expressions rather than statements. You can see here that we could assign the -closure to a variable, and needed to terminate the line with a semicolon. -Frequently, closures are defined and passed into functions without ever giving -them names. +We only get `5`. Rather than taking a mutable borrow out on our `num`, we took +ownership of a copy. + +One of the most common places you'll see the `move` keyword used is with threads. +We'll talk more about that in Chapter XX. -This closure is capturing the x variable. But how is it doing that? What if we -define a closure in a function and return it? What if we change x between where -the closure is defined and where it's executed? +### Closures, ownership, and borrowing -By default, closures capture the variables by reference. This means that -closures cannot outlive variables that they capture. If we try to compile the -following code: +Remember Listing 10.8 from Chapter 10.3? It looked like this: ```rust,ignore -fn main() { - let closure; - { - let x = 4; - closure = ||{ x }; // Closure that takes no arguments and returns x - } +{ + let r; + + { + let x = 5; + r = &x; + } + + println!("r: {}", r); } ``` -We get an error because `x` does not live long enough. +This example doesn't compile, becuase `x` doesn't have a long enough lifetime. +Becuase closures may borrow variables from their enclosing scope, we can +construct a similar example with closures. It also won't compile: + +```rust,ignore +{ + let closure; + + { + let x = 4; + + closure = || x ; // A closure that takes no arguments and returns x. + } +} +``` -We can make closures move (or copy, for types declared Copy) their values in by -using the `move` keyword: +We get an error because `x` does not live long enough: + +```text +error: `x` does not live long enough + --> :8:22 + | +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 +``` + +In this instance, we can use the `move` keyword from the last section +to have the closure take `x` by value, and since `x` is a number, it +is `Copy` and therefore will be copied: ```rust -fn main() { - let closure; - { - let x = 4; - closure = move ||{ x }; // Closure that takes no arguments and returns x - } -} -``` - -### `Fn` traits - -It's clear that closures are more than functions, because closures have to keep -track of what variables they've captured. Closures are essentially functions -that come with a struct that includes their captured variables (or references). -This means that every closure is a different type. If we want a function to -take a closure as an argument, we can't simply talk about a "closure" type, -because each one is a different type. The way that Rust manages this is with -traits. There are three traits that can be automatically applied to each -closure, depending on what the closure is doing: `FnOnce`, `FnMut`, and `Fn`. -These traits derive from each other, so if a type is `FnMut`, it must also be -`FnOnce`, and if a type is `Fn`, it must be both `FnMut` and `FnOnce`. Closures -automatically derive from the ones that are appropriate, and you cannot -currently derive them for your custom types. - -If you want to write a function such as `map` that takes in a function, you -should almost always take in one of the `Fn` traits. The `FnOnce` trait defines -a function `call_once` that consumes `self`. It's the most general option, and -if your function will only call the given function once, it should take in an -`FnOnce`. `FnMut` is the next most general, since its `call_mut` function takes -`self` by mutable reference, so you need a mutable reference to the closure to -call it. `Fn` is the most specific of these, but you only need a immutable -reference to call a function that is `Fn`. - -All functions and closures implement `FnOnce`. If a closure takes its variables -by reference rather than by move, then it also implements `FnMut`. If the -closure modifies none of its captures, then it also implements `Fn`. All -functions also implement `Fn` because they don't capture at all. +{ + let closure; + + { + let mut x = 4; + + closure = move || x ; // A closure that takes no arguments and returns x. + + x = 5; + + assert_eq!(closure(), 4); + } +} +``` + +Even though we modified `x`, since `closure` now has its own version, our +changes to `x` won't change the version of `x` that's in the closure. + +Rust doesn't provide a way to say "these variables must be captured like this"; +it's either all by inference, or all by value. However, you can accomplish +this sort of goal by combining `move` with some extra bindings: + +```rust +let s1 = String::from("hello"); +let s2 = String::from("hello"); + +// We want to capture s1 by reference, but s2 by value. What to do? First, make +// an extra binding for s1: + +let r = &s1; + +// Then make it a `move` closure: + +let calculation = move || { + // ... and use them inside. That's the trick: r is captured, but it's a + // reference; so we've effectively taken s1 by reference and s2 by move. + r; + s2; +}; +``` + +### Accepting closures as arguments with the `Fn` traits + +While we can bind closures to variables, that's not the most useful thing we +can do with them. We can also take closures as arguments to functions. We can +do that with the `Fn` traits. Like this: + +```rust +fn call_with_one(some_closure: F) -> i32 + where F: Fn(i32) -> i32 { + + some_closure(1) +} + +let answer = call_with_one(|x| x + 2); + +assert_eq!(3, answer); +``` + +We pass our closure, `|x| x + 2`, to `call_with_one`. It does what it suggests: +it calls the closure, giving it `1` as an argument. + +Let's examine the signature of `call_with_one` in more depth: + +```rust +fn call_with_one(some_closure: F) -> i32 +# where F: Fn(i32) -> i32 { +# some_closure(1) } +``` + +We take one parameter, and it has the type `F`. We also return an `i32`. This +part isnt interesting. The next part is: + +```rust +# fn call_with_one(some_closure: F) -> i32 + where F: Fn(i32) -> i32 { +# some_closure(1) } +``` + +The `Fn` trait represents a closure. We can use it as a bound for our generic +type. In this case, our closure takes an `i32` as an argument and returns an +`i32`, and so the generic bound we use is `Fn(i32) -> i32`. + +Why a trait? Well, each closure has a unique type. Becuase of this, we can't +write the type of a closure directly, we have to use generics. + +`Fn` isn't the only trait, however, there are three. `Fn`, `FnMut`, and +`FnOnce`. This continues the patterns of threes we've seen elsewhere in Rust: +by owner, by reference, and by mutable reference. By using `Fn`, you may only +refer to things in its environment by reference. If you mutate the environment, +you must use `FnMut`, and if you take ownership of the environment, `FnOnce`. +Most of the time, you can write `Fn`, and then the compiler will tell you if +you need `FnMut` or `FnOnce`. ## Iterators -Iterators are types that implement the `Iterator` trait. Iterators are designed -to return a sequence of values by repeatedly calling their `next()` method. -Often, these values come from a data structure such as `Vec`. Iterators are -powerful, however, because they can be used for more than that. For example, -you can have an infinite iterator -- one whose `next()` method never returns -`None`. There are also functions on iterators like `map`, which applies a -function to each value in the iterators as they're requested. `map` has low -memory usage because it only applies the function as elements are requested, so -the whole sequence does not need to build up. - -### Iterator & for loop - -`for` loops are usually said to "iterate" over things, so it makes perfect -sense that in Rust, for loops use iterators. Specifically, you can say `for x -in y` if `y` implements the `IntoIterator` trait with the `into_iter` method. -This method consumes `self` and (for data structures) returns an iterator that -returns the values in the data structure. - -Common `IntoIterator` types are data structures and ranges such as `0..10`. -Thus, when you say `for i in 0..10`, you are creating a `Range` that knows its -start and end, then calling `into_iter` on it to convert it to an iterator, and -then repeatedly calling `next()` on that iterator to get the numbers 0, 1, ..., -9. +Let's move on to another topic: iterators. Iterators are a pattern in Rust +that allows you to do some processing on a sequence of items. Like this: + +```rust +let v1 = vec![1, 2, 3]; + +let v2: Vec = v1.iter().map(|x| x + 1).collect(); + +assert_eq!(v2, [2, 3, 4]); +``` + +That second line is full of new concepts. Let's check each bit out, in turn: + +```rust,ignore +v1.iter() +``` + +The `iter` method on vectors allows us to produce an *iterator* from the +vector. This iterator has a number of its own methods. The next section +is one of those: + +```rust,ignore +.map(|x| x + 1) +``` + +The `map` method on an iterator allows us to process each element: for every +element `x`, we add one to it. `map` is one of the most basic ways of +interacting with an iterator, as processing each element in turn is very +useful! + +`map` itself is sometimes called an *iterator adapter*, that is, it's a method +on an iterator that produces a new iterator. That is, `map` builds on top of +our previous iterator and produces another one, by calling the closure it's +passed to produce the new sequence of values. There are many useful iterator +adapters, but before we talk about them, let's get to the last bit: + +```rust,ignore +.collect() +``` + +The `collect` method consumes an iterator, and puts them into a new data +structure. In this case, since we've said that `v2` has the type `Vec`, it +will create a new vector out of them. + +So, to recap: + +1. Create an iterator from the vector. +2. Use the `map` adaptor to add one to each element. +3. Use the `collect` adaptor to consume the iterator and make a new vector. + +That's how we end up with `[2, 3, 4]`. As you can see, closures are a very +important part of using iterators; they provide the way of customizing the +behavior of an iterator adapter like `map`. ### Iterators are Lazy -An important and very useful fact about iterators is that they are _lazy_. This -means that, in general, iterators don't look at the things they're iterating -over until they are about to return it. This is very useful when your iterator -will return a large (or perhaps even infinite!) sequence. Another consequence -of this is that you must be careful when using the `map` function with a -closure that mutates things. The closure will not be executed on any elements -until the resulting iterator is consumed. +In the previous section, you may have noticed a subtle bit of wording: we +said that `map` adapts an iterator, but that `collect` 'consumes' one. That +was intentional. By itself, iterators won't do anything; that is, they're +lazy. That is, if we write code like this: -Iterator adapters are iterators that are based off of other iterators. A simple -iterator adapter in the standard library is the -[take](https://doc.rust-lang.org/std/iter/struct.Take.html) adapter. This -iterator contains a counter initialized with a user-defined number and another -iterator. When its `next()` method is called, it decrements the count, and if -it's 0, it returns `None`. Otherwise, it returns `next()` of its contained -iterator. In this way, we _adapt_ the inner iterator to only return the first -`n` elements rather than everything. +```rust +let v1: Vec = vec![1, 2, 3]; -### Implementing the Iterator trait +v1.iter().map(|x| x + 1); // without collect +``` + +It will compile, but it will give us a warning: -Talk about using Associated Types here, foreshadow to advanced type systems -chapter about why this is a different thing than normal +```text +error: unused result which must be used: iterator adaptors are lazy and do + nothing unless consumed + --> :5:1 + | +5 | v1.iter().map(|x| x + 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +``` -## ??? How does this improve the I/O project from Chapter 12 +That is, iterator adaptors won't start actually doing the processing on their +own. They need some sort of adaptor that causes the iterator chain to evaluate. +We call those 'consuming adaptors', and `collect` is one of them. -Does this get woven into the above sections? +So, with these different kinds of iterator adaptors... how do we tell which +ones consume or not? And what adaptors are available? For that, let's look at +the `Iterator` trait. + +### The Iterator trait + +Iterators all implement a trait, `Iterator`, that is defined in the standard +library. It looks like this: + +```rust +trait Iterator { + type Item; + + fn next(&mut self) -> Option; +} +``` + +There's some new syntax that we haven't covered here yet, and that's the `type +Item` and `Self::Item` bits. This is called an "associated type", and we'll +talk about them in Chapter XX. For now, all you need to know is that this +says that the `Iterator` trait requires that you also define an `Item` type, +and that its type is used in the return type of `next`. The `Item` type will +be the type of element that's returned from the iterator. You'll learn more +about why this is needed in that chapter. + +Let's make an iterator named `Counter` which counts from `1` to `5`, using +this trait. First, we need to create a struct that holds the state for +our iterator: + +```rust +struct Counter { + count: usize, +} + +impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } +} +``` + +The `new` method here isn't strictly neccesary, but we want our `Counter` +to go from one to five, so we're going to initialize it with a zero. Let's +see why by implementing `Iterator` for it: + +```rust,ignore +impl Iterator for Counter { + // Our iterator will produce u32s + type Item = u32; + + fn next(&mut self) -> Option { + // increment our count. This is why we started at zero. + self.count += 1; + + // check to see if we've finished counting or not. + if self.count < 6 { + Some(self.count) + } else { + None + } + } +} +``` + +There's a lot going on here! First of all, we assign `Item` to be `u32`. Remember +that we don't really understand this syntax yet, you'll just have to trust me. + +Next, we implement the `next` method. This method is the main interface into an +iterator: it returns an option. If the option is `Some(value)`, we have gotten +another value from the iterator. If it's `None`, we know that iteration is +finished. Inside of it, we do whatever kind of calculation our iterator needs. +In this case, we add one, and then check to see if we're still below six. If we +are, we can return `Some(self.count)`, to give the next value, but if we're at +six or more, iteration is over, and we return `None`. + +Once we've implemented this, we have an iterator! We can use it by calling +`next` repeatedly: + +```rust,ignore +let mut counter = Counter::new(); + +let x = counter.next().unwrap(); +println!("{}", x); + +let x = counter.next().unwrap(); +println!("{}", x); + +let x = counter.next().unwrap(); +println!("{}", x); + +let x = counter.next().unwrap(); +println!("{}", x); + +let x = counter.next().unwrap(); +println!("{}", x); +``` + +This will print `1` through `5`, each on their own line. + +Calling `next()` this way gets repetitive. Rust has a construct which can call +`next()` on your iterator, until it reaches `None`. Let's go over that next. + +### IntoIterator and for loops + +When we said that iterators are important to Rust, we weren't joking: iterators +are how `for` loops work under the hood! Remember this `for` loop from Chapter 3? + +```rust +fn main() { + let a = [10, 20, 30, 40, 50]; + + for element in a.iter() { + println!("the value is: {}", element); + } +} +``` + +At the time, we didn't explain the `.iter()` bit, but now you know that it +makes an iterator. Rust's `for` loop is actually 'synatx sugar', that is, it's +special syntax, but we could write it ourselves. It's just a bit nicer to write +by using `for`. If we took the code above and expanded it, it would look like +this: + + +```rust,ignore +let a = [10, 20, 30, 40, 50]; + +{ + let result = match IntoIterator::into_iter(a) { + mut iter => loop { + match iter.next() { + Some(element) => { println!("the value is: {}", element); }, + None => break, + } + }, + }; + result +} +``` + +Whew! This code is very compact, and uses a lot of concepts. We've talked +about them all already though, so let's break it down: + +```rust,ignore +let result = match IntoIterator::into_iter(a) { +``` + +`IntoIterator` is another trait that we haven't discussed yet. As the name +suggests, it has an `into_iter` method that takes one argument, and turns +that argument into an `Iterator`. This means that we can pass anything +that's can be converted into an iterator to a `for` loop, and it will +just work. That's nice! However, arrays do not implement `IntoIterator`, +and so we had to call the `iter` method ourself. But since that returns +an iterator, calling `into_iter` on it does nothing, so we're still good! + +We're also `match`ing on the iterator that's returned. Let's look at how +that works: + +```rust,ignore +mut iter => loop { +``` + +The `match` only has one arm: we match the result, binding it to a mutable +binding, `iter`, and then immediately call `loop` to loop forever. What's +in the body of that loop? Another `match!` + +```rust,ignore +match iter.next() { + Some(element) => { println!("the value is: {}", element); }, + None => break, +} +``` + +Here, we call `next()`, and see if it's `Some`. If it is, then we call +the body of the `for` loop, which in this case is a single `println!`. +If we got `None` back from the iterator, we `break` out of the loop. + +### IntoIterator and vectors + +Let's talk a bit more about `IntoIterator`. As we said above, it's job is to +convert something into an iterator. You'll find it implemented on all kinds of +handy things. Consider this example: + +```rust +let v = vec![1, 2, 3]; + +for e in v { + println!("iterating by owner"); +} + +let v = vec![1, 2, 3]; + +for e in &v { + println!("iterating by reference"); +} + +let mut v = vec![1, 2, 3]; + +for e in &mut v { + println!("iterating by mutable reference"); +} +``` + +Whoah! The standard library implements `IntoIterator` on vectors directly, +allowing you to take ownership of each element of the vector. But it also +implements it on `&Vec` and `&mut Vec`, which allow you to iterate over +references and mutable references, respectively. Since the `for` loop +implicitly calls `IntoIterator::into_iter` for us, we don't need to do +anything. It just works. + +`IntoIterator` is a very useful trait, but we should move on. You can find more +about it in its documentation. + +### All sorts of adaptors + +So we implemented `Iterator` for our `Counter`, but we only wrote `next`. +When we used iterators at the start of this section, we had other methods, +like `map` and `collect`. What about those? + +Well, when we told you about the definition of `Iterator`, we committed a +small lie of omission. The `Iterator` trait has a number of other useful +methods. Like `map`: + +```rust,ignore +fn map(self, f: F) -> Map where + Self: Sized, F: FnMut(Self::Item) -> B; +``` + +And `collect`: + +```rust,ignore +fn collect>(self) -> B where Self: Sized; +``` + +Both of these type signatures make advanced usages of generics, so don't +worry about fully understanding them right now. The point is this: all +of these other methods on `Iterators` are implemented for you, in terms +of `next`. That is, you only need to implement `next`, and you get +all of the other adaptors for free. There are a lot of them! + +```rust +let sum: u64 = (1..).zip(2..) + .map(|(a, b)| a + b) + .filter(|&x| x < 100) + .take(5) + .sum(); + +assert_eq!(35, sum); +``` + +The `1..` and `2..` are ranges, but they work as infinite iterators: they'll +count from one and two until infinity. The `zip` adaptor zips two iterators +together, producing a new iterator which gives you tuples. The first element is +from the first iterator, the second is from the second iterator. We then take +that iterator, and add the two numbers together. We then filter out only the +sums that are less than 100, and then finally, take the first five of those +numbers. Finally, `sum` will add up all of the numbers into one last number. +This is kind of a silly calculation, but it shows off a few different iterator +adaptors, you can do all kinds of things! Check the documentation of `Iterator` +to see them all. Some crates you may use in the ecosystem might add even more +adaptors as well. + +## Improving our I/O project + +Let's use what we learned to improve our project from the last chapter. Back +in listing 12-5, we had this code: + +```rust,ignore +fn parse_config(args: &[String]) -> Config { + let search = args[1].clone(); + let filename = args[2].clone(); + + Config { + search: search, + filename: filename, + } +} +``` + +At the time, we told you to not worry about the `clone` calls here, and that +we could remove them in the future. Well, that time is now! Later in that +section, we moved this code into `Config::new`, and it looked like this: + +```rust,ignore +impl Config { + fn new(args: &[String]) -> Result { + if args.len() < 3 { + return Err("not enough arguments"); + } + + let search = args[1].clone(); + let filename = args[2].clone(); + + Ok(Config { + search: search, + filename: filename, + }) + } +} +``` + +Let's fix that version, as it's the final form of the code we had. So, why do +we need `clone` here? The issue is, we have a slice of `String`s in `args`, +that is, we don't own them. So the only thing we can do is copy them. But now +that we know more about iterators, we can make this work. Let's modify +`new` to take a different kind of argument: + +```rust,ignore +fn new(args: std::env::Args) -> Result { +``` + +`std::env::Args` is the return type of `the `std::env::args` function. +It's an iterator! Let's modify `main` to pass it in directly, rather than +using `collect` to make a vector: + +```rust,ignore +fn main() { + let config = Config::new(env::args()); +``` + +Much simpler. Now we need to fix the body. We know that `next` will give +us the next value of the iterator. Let's use that: + +```rust,ignore +impl Config { + fn new(mut args: std::env::Args) -> Result { + // The first argument is the program name, let's ignore that + args.next(); + + let search = match args.next() { + Some(arg) => arg, + None => return "Didn't get a search string", + }; + + let filename = match args.next() { + Some(arg) => arg, + None => return "Didn't get a file name", + }; + + Ok(Config { + search: search, + filename: filename, + }) + } +} +``` + +Here, we use `next` to get the arguments out of `Args`. This code is way +better: we now will not `panic!` when we get too few arguments, but instead +return a `Result` with a meaningful error message. One slightly +unfortunate bit is the repetition with the `match`. `?` only works on +`Result`s, and not `Option`s at the moment. It also won't copy +the `String`s, as we can get them directly from the iterator, rather than +the slice we had before. Given that `Args` produces its arguments by value, +we move them instead. Another win! + +The other bit was in our version of `grep`: + +```rust +fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { + let mut results = Vec::new(); + + for line in contents.lines() { + if line.contains(search) { + results.push(line); + } + } + + results +} +``` + +We can write this code like this instead: + +```rust +fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { + contents.lines() + .filter(|line| line.contains(search)) + .collect() +} +``` + +Wow, much shorter! Here, we use the `filter` adapter to only select +the lines that `contains(search)` returns true for. We then collect +them up into another vector with `collect`. Much simpler! + +We can do the same for `grep_case_insensitive`: + +```rust +fn grep_case_insensitive<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { + contents.lines() + .filter(|line| { + line.to_lowercase().contains(&search) + }).collect() +} +``` + +Not too bad! So which style should you chose? Most Rust programmers prefer to +use the iterator style. It's a bit tougher to understand at first, but once you +gain an intuition for what the various iterator adaptors do, this is much +easier to understand. Instead of fiddling with the various bits of looping +and building a new vector, it focuses on the high-level objective of the loop. + +But are they truly equivalent? Surely the more low-level loop will be faster? +Let's talk about performance. ## Summary: Performance -### Iterators compile down to ASM == for loop +Which version of our `grep` is faster, the one with an explicit `for` loop, +or iterators? We ran a quick benchmark by loading the entire contents of +"The Adventures of Sherlock Holmes" by Sir Arthur Conan Doyle into a `String` +and looking for the word `the` in it. Here were the times: + +```text +test bench_grep_for ... bench: 19,620,300 ns/iter (+/- 915,700) +test bench_grep_iter ... bench: 19,234,900 ns/iter (+/- 657,200) +``` -Most complicated chain of iterator functions that compile down to the same ASM as a for loop +That's right, the iterator version ended up slightly faster! We're not going +to share the bencharmark code exactly here, as the point is not to prove that +they're exactly equivalent. For a _real_ benchmark, you'd want to check various +texts of various sizes, different words, words of different lengths, and all +kinds of other things. The point here is this: iterators, while a high-level +abstraction, get compiled down to roughly the same code as if you'd written +the lower-level code yourself. -### Representation: Closures are a Struct +As another example, here's an iterator chain that does some math: + +```rust,ignore +// We have these three variables in scope: +let buffer: &mut [i32]; +let coefficients: [i64; 12]; +let qlp_shift: i16; + +for i in 12..buffer.len() { + let prediction = coefficients.iter() + .zip(&buffer[i - 12..i]) + .map(|(&c, &s)| c * s as i64) + .sum::() >> qlp_shift; + let delta = buffer[i]; + buffer[i] = prediction as i32 + delta; +} +``` -Closures don't have any further performance penalty over regular fn calls +This code sample is taken from an audio decoder. It "restores sample values +from residues," if that means anything to you. The point is, doing math is +something that often needs to be done very quickly, so you care about speed. +But here, we're creating an iterator, using two adaptors, and then finally +consuming the value. What would this code compile to? Well, as of this writing, +this, it compiles down to the same assembly you'd write by hand: there's no +loop at all, as it knows that there are twelve iterations, and so it "unrolls" +the loop. All of the coefficients get stored in registers (read: they're very +fast). There are no bounds checks on the array access. It's extremely +efficient. + +Now that you know this, go use iterators and closures without fear! They can +really make code feel more high-level, but don't have a performance penalty for +doing so. From 09dd956ed23f492358a2bb3313c67fa2b6e7de12 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 17 Jan 2017 22:11:06 -0500 Subject: [PATCH 08/18] Edits to the intro and Closures section --- src/ch13-00-functional-features.md | 489 +++++++++++++++++++---------- 1 file changed, 317 insertions(+), 172 deletions(-) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index d7e8a4d72a..6a35de76ef 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -1,216 +1,341 @@ # Functional Language features in Rust - Iterators and Closures -As a language, Rust takes influence from a lot of places. One of those places -is functional programming. We won't use this chapter to debate what exactly -'functional programming' is, but instead, show off some features of Rust that -are similar to many languages that are referred to as functional. +Rust's design has taken inspiration from a lot of previous work. One of Rust's +influences is functional programming, where functions are values that can be +used as arguments or return values to other functions, assigned to variables, +and so forth. We're going to sidestep the issue of what, exactly, function +programming is or is not, and instead show off some features of Rust that +are similar to features in many languages referred to as functional. More specifically, we're going to cover: -* Closures, a function-like construct you can store in a binding. -* Iterators, a way of processing series of elements. -* Using these features to improve upon the project from the last chapter. -* A bit about these features' performance. Spoiler alert: they're faster than - you might think! +* *Closures*, a function-like construct you can store in a variable. +* *Iterators*, a way of processing series of elements. +* How to use these features to improve upon the project from the last chapter. +* The performance of these features. Spoiler alert: they're faster than you + might think! -This is not a complete list of Rust's influence from the functional style; +This is not a complete list of Rust's influence from the functional style: pattern matching, enums, and many other features are too. But mastering -closures and iterators will help you write idiomatic, fast Rust code. +closures and iterators are an important part of writing idiomatic, fast Rust +code. ## Closures -Rust gives you the ability to define *closures*, which are sort of like -functions. Instead of giving you a technical definintion, let's dive into -what clousures look like, syntactically: +Rust gives you the ability to define *closures*, which are similar to +functions. Instead of starting with a technical definintion, let's see what +clousures look like, syntactically, and then we'll return to defining what they +are. Listing 13-1 shows a small closure whose definition is assigned to the +variable `add_one`, which we can then use to call the closure: + +
+Filename: src/main.rs ```rust -let add_one = |x| x + 1; +fn main() { + let add_one = |x| x + 1; -let five = add_one(4); + let five = add_one(4); -assert_eq!(5, five); + assert_eq!(5, five); +} ``` -The first line defines a closure, and binds it to the `add_one` variable. The -arguments to the closure go in between the pipes (`|`). +
+ +Listing 13-1: A closure that takes one parameter and adds one to it, assigned to +the variable `add_one` + +
+
-This is a simple closure with only one expression as its body, let's see one -that's got a bit more going on: +The closure definition, on the first line, shows that the closure takes one +parameter named `x`. Parameters to closures go in between vertical pipes (`|`). + +This is a minimal closure with only one expression as its body. Listing 13-2 has +a closure with a bit more complexity: + +
+Filename: src/main.rs ```rust -let calculate = |a, b| { - let mut result = a * 2; +fn main() { + let calculate = |a, b| { + let mut result = a * 2; - result += b; + result += b; - result -}; + result + }; -assert_eq!(7, calculate(2, 3)); // 2 * 2 + 3 == 7 -assert_eq!(13, calculate(4, 5)); // 4 * 2 + 5 == 13 + assert_eq!(7, calculate(2, 3)); // 2 * 2 + 3 == 7 + assert_eq!(13, calculate(4, 5)); // 4 * 2 + 5 == 13 +} ``` -We can use `{}`s to give a closure a body with more than one expression. +
+ +Listing 13-2: A closure with two parameters and multiple expressions in its body + +
+
+ +We can use curly brackets to define a closure body with more than one +expression. + +You'll notice a few things about closures that are different from functions +defined with the `fn` keyword. The first difference is that we did not need to +annotate the types of the parameters the closure takes or the value it returns. +We can choose to add type annotations if we want; Listing 13-3 shows the +closure from Listing 13-1 with annotations for the parameter's and return +value's types: -You'll notice a few things about closures that are a bit different from -functions defined with `fn`. The first is that we did not need to annotate the -types of arguments the closure takes or the values it returns. We can: +
+Filename: src/main.rs ```rust -let plus_one = |x: i32| -> i32 { x + 1 }; +fn main() { + let add_one = |x: i32| -> i32 { x + 1 }; + + assert_eq!(2, add_one(1)); +} +``` + +
-assert_eq!(2, plus_one(1)); +Listing 13-3: A closure definition with optional parameter and return value +type annotations + +
+
+ +The syntax of closures and functions looks more similar with type annotations. +Let's compare the different ways we can specify closures with the syntax for +defining a function more directly. We've added some spaces here to line up the +relevant parts: + +```rust +fn add_one_v1 (x: i32) -> i32 { x + 1 } // a function +let add_one_v2 = |x: i32| -> i32 { x + 1 }; // the full syntax for a closure +let add_one_v3 = |x: i32| { x + 1 }; // a closure eliding types +let add_one_v4 = |x: i32| x + 1 ; // without braces ``` -But we dont have to. Why is this? Functions are part of an explicit interface -that's exposed to your users, so defining this interface rigidly is helpful. -But closures aren't used like this: they're stored in bindings and called -directly. Being forced to annotate the types would be a significant ergonomic -loss for little advantage. +The reason type annotations are not required for defining a closure but are +required for defining a function is that functions are part of an explicit +interface exposed to your users, so defining this interface rigidly is +important for ensuring that everyone agrees on what types of values a function +uses and returns. Closures aren't used in an exposed interface like this, +though: they're stored in bindings and called directly. Being forced to +annotate the types would be a significant ergonomic loss for little advantage. + +Closure definitions do have one type inferred for each of their parameters and +for their return value. For instance, if we call the closure without type +annotations from Listing 13-1 using an `i8`, we'll get an error if we then try +to call the same closure with an `i32`: -The syntax is similar, but a bit different. Let's compare more directly. We've -added some spaces here to line up the relevant parts: +Filename: src/main.rs ```rust -fn plus_one_v1 (x: i32) -> i32 { x + 1 } // a function -let plus_one_v2 = |x: i32| -> i32 { x + 1 }; // the full syntax for a closure -let plus_one_v3 = |x: i32| { x + 1 }; // a closure eliding types -let plus_one_v4 = |x: i32| x + 1 ; // without braces +let add_one = |x| x + 1; + +let five = add_one(4i8); +assert_eq!(5i8, five); + +let three = add_one(2i32); ``` -Small differences, but they're similar. Why come up with a different syntax -for closures? There's one additional way in which they're different from -functions: they posses an 'environment'. +The compiler gives us this error: -## Closures and their environment +```text +error[E0308]: mismatched types + --> + | +7 | let three = add_one(2i32); + | ^^^^ expected i8, found i32 +``` + +Since closures' types can be inferred reliably since they're called directly, +it would be tedious if we were required to annotate their types. + +Another reason to have a different syntax from functions for closures is that +they have different behavior than functions: closures possess an *environment*. + +### Closures Can Reference Their Environment We've learned that functions can only use variables that are in scope, either -by being static or being declared as parameters. But closures can do more. -They can access variables from their enclosing scope. Like this: +by being static or being declared as parameters. Closures can do more: they're +allowed to access variables from their enclosing scope. Listing 13-4 has an +example of a closure in the variable `equal_to_x` that uses the variable `x` +from the closure's surrounding environment: +
+Filename: src/main.rs ```rust fn main() { let x = 4; - + let equal_to_x = |z| z == x; - + let y = 4; - + assert!(equal_to_x(y)); } ``` -Here, even though `x` is not an argument to `equal_to_x`, it's able to -refer to it, as it's a variable defined in the same scope. We couldn't -do this with a `fn`. If we tried... +
+ +Listing 13-4: Example of a closure that refers to a variable in its enclosing +scope + +
+
+Here, even though `x` is not one of the parameters of `equal_to_x`, the +`equal_to_x` closure is allowed to use `x`, since `x` is a variable defined in +the same scope that `equal_to_x` is defined. We aren't allowed to do the same +thing that Listing 13-4 does with functions; let's see what happens if we try: + +Filename: src/main.rs ```rust,ignore fn main() { let x = 4; - - fn equal_to_x(z) { z == x } - + + fn equal_to_x(z: i32) -> bool { z == x } + let y = 4; - + assert!(equal_to_x(y)); } ``` -We'd get an error: +We get an error: ```text -error: can't capture dynamic environment in a fn item; use the || { ... } -closure form instead [E0434] -z == x - ^ +error[E0434]: can't capture dynamic environment in a fn item; use the || { ... } +closure form instead + --> + | +4 | fn equal_to_x(z: i32) -> bool { z == x } + | ^ ``` -This only works with closures! This property is also subject to all of the -usual rules around ownership and borrowing. Because closures attempt to infer -the types of their arguments, they also have to infer how they're borrowed. -They'll do that from how they are used. Consider this example: +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 borrow(f: &Foo) { - println!("Took foo by reference."); +fn borrows(f: &Foo) { + println!("Took {:?} by reference.", f); } -fn borrow_mut(f: &mut Foo) { - println!("Took foo by mutable reference."); +fn borrows_mut(f: &mut Foo) { + println!("Took {:?} by mutable reference.", f); } fn moves(f: Foo) { - println!("Took foo by ownership."); + println!("Took ownership of {:?}.", f); } -let f = Foo; -let borrows = |f| borrow(f); -borrows(&f); +fn main() { + let f1 = Foo; + let closure_that_borrows = |x| borrows(x); + closure_that_borrows(&f1); -let mut f = Foo; -let borrows_mut = |f| borrow_mut(f); -borrows_mut(&mut f); + let mut f2 = Foo; + let closure_that_borrows_mut = |y| borrows_mut(y); + closure_that_borrows_mut(&mut f2); -let f = Foo; -let moves = |f| moves(f); -moves(f); + let f3 = Foo; + let closure_that_moves = |z| moves(z); + closure_that_moves(f3); +} ``` -Here, Rust is able to look at how we use `f` inside of each closure. If we pass -it to a function that takes `&Foo`, then the type of `f` must be `&Foo`. If we -pass it to a function that takes `&mut Foo`, then the type of `f` must be `Foo`. -And so on. +
+ +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`. -### The `move` keyword +### Overriding Inferred Borrowing with the `move` Keyword -Rust will allow you to override this inference with the `move` keyword. This -will cause all variables to be taken by ownership, instead of whatever they -were inferred as. Consider this: +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 = 5; +let mut num = 4; { let mut add_num = |x| num += x; - add_num(5); + add_num(6); } assert_eq!(10, num); ``` -So in this case, our closure took a mutable reference to `num`, and then when -we called `add_num`, it mutated the underlying value, as we'd expect. We also -needed to declare `add_num` as `mut` too, because were mutating its -environment. +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 to a `move` closure, its different: +If we change the definition of `add_num` to a `move` closure, the behavior is +different: ```rust -let mut num = 5; +let mut num = 4; { let mut add_num = move |x| num += x; - add_num(5); + add_num(6); } -assert_eq!(5, num); +assert_eq!(4, num); ``` -We only get `5`. Rather than taking a mutable borrow out on our `num`, we took -ownership of a copy. +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. -We'll talk more about that in Chapter XX. +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, ownership, and borrowing +### Closures and Lifetimes -Remember Listing 10.8 from Chapter 10.3? It looked like this: +Remember Listing 10-8 from the Lifetime Syntax section of Chapter 10? It looked +like this: ```rust,ignore { @@ -225,27 +350,38 @@ Remember Listing 10.8 from Chapter 10.3? It looked like this: } ``` -This example doesn't compile, becuase `x` doesn't have a long enough lifetime. -Becuase closures may borrow variables from their enclosing scope, we can -construct a similar example with closures. It also won't compile: +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:22 + --> | 8 | closure = || x ; // A closure that takes no arguments and returns x. | -- ^ does not live long enough @@ -257,57 +393,73 @@ error: `x` does not live long enough | - borrowed value needs to live until here ``` -In this instance, we can use the `move` keyword from the last section -to have the closure take `x` by value, and since `x` is a number, it -is `Copy` and therefore will be copied: +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); + assert_eq!(closure(), 4); } } ``` -Even though we modified `x`, since `closure` now has its own version, our -changes to `x` won't change the version of `x` that's in the closure. +
+ +Listing 13-7: Moving a value into the closure to fix the lifetime error -Rust doesn't provide a way to say "these variables must be captured like this"; -it's either all by inference, or all by value. However, you can accomplish -this sort of goal by combining `move` with some extra bindings: +
+
+ +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("hello"); - -// We want to capture s1 by reference, but s2 by value. What to do? First, make -// an extra binding for s1: +let s2 = String::from("goodbye"); let r = &s1; -// Then make it a `move` closure: - let calculation = move || { - // ... and use them inside. That's the trick: r is captured, but it's a - // reference; so we've effectively taken s1 by reference and s2 by move. r; s2; }; + +println!("Can still use s1 here but not s2: {}", s1); ``` -### Accepting closures as arguments with the `Fn` traits +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`. + +### Closures as Function Parameters Using the `Fn` Traits While we can bind closures to variables, that's not the most useful thing we -can do with them. We can also take closures as arguments to functions. We can -do that with the `Fn` traits. Like this: +can do with them. We can also define functions that have closures as parameters +by using the `Fn` traits. Here's an example of a function named `call_with_one` +whose signature has a closure as a parameter: ```rust fn call_with_one(some_closure: F) -> i32 @@ -321,40 +473,33 @@ let answer = call_with_one(|x| x + 2); assert_eq!(3, answer); ``` -We pass our closure, `|x| x + 2`, to `call_with_one`. It does what it suggests: -it calls the closure, giving it `1` as an argument. - -Let's examine the signature of `call_with_one` in more depth: - -```rust -fn call_with_one(some_closure: F) -> i32 -# where F: Fn(i32) -> i32 { -# some_closure(1) } -``` - -We take one parameter, and it has the type `F`. We also return an `i32`. This -part isnt interesting. The next part is: - -```rust -# fn call_with_one(some_closure: F) -> i32 - where F: Fn(i32) -> i32 { -# some_closure(1) } -``` - -The `Fn` trait represents a closure. We can use it as a bound for our generic -type. In this case, our closure takes an `i32` as an argument and returns an -`i32`, and so the generic bound we use is `Fn(i32) -> i32`. - -Why a trait? Well, each closure has a unique type. Becuase of this, we can't -write the type of a closure directly, we have to use generics. - -`Fn` isn't the only trait, however, there are three. `Fn`, `FnMut`, and -`FnOnce`. This continues the patterns of threes we've seen elsewhere in Rust: -by owner, by reference, and by mutable reference. By using `Fn`, you may only -refer to things in its environment by reference. If you mutate the environment, -you must use `FnMut`, and if you take ownership of the environment, `FnOnce`. -Most of the time, you can write `Fn`, and then the compiler will tell you if -you need `FnMut` or `FnOnce`. +We pass the closure `|x| x + 2`, to `call_with_one`, and `call_with_one` calls +that closure with `1` as an argument. The return value of the call to +`some_closure` is then returned from `call_with_one`. + +The signature of `call_with_one` is using the `where` syntax discussed in the +Traits section of Chapter 10. The `some_closure` parameter has the generic type +`F`, which in the `where` clause is defined as having the trait bounds +`Fn(i32) -> i32`. The `Fn` trait represents a closure, and we can add types to +the `Fn` trait to represent a specific type of closure. In this case, our +closure has a parameter of type `i32` and returns an `i32`, so the generic bound +we specify is `Fn(i32) -> i32`. + +Specifying a function signature that contains a closure requires the use of +generics and trait bounds. Each closure has a unique type, so we can't write +the type of a closure directly, we have to use generics. + +`Fn` isn't the only trait bound available for specifying closures, however. +There are three: `Fn`, `FnMut`, and `FnOnce`. This continues the patterns of +threes we've seen elsewhere in Rust: borrowing, borrowing mutably, and +ownership. Using `Fn` specifies that the closure used may only borrow values in +its environment. To specify a closure that mutates the environment, use +`FnMut`, and if the closure takes ownership of the environment, `FnOnce`. Most +of the time, you can start with `Fn`, and the compiler will tell you if you +need `FnMut` or `FnOnce` based on the closure values passed into the function. + +To illustrate a situation where it's useful for a function to have a parameter +that's a closure, let's move on to our next topic: iterators. ## Iterators @@ -687,7 +832,7 @@ let sum: u64 = (1..).zip(2..) .filter(|&x| x < 100) .take(5) .sum(); - + assert_eq!(35, sum); ``` @@ -819,7 +964,7 @@ We can write this code like this instead: fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { contents.lines() .filter(|line| line.contains(search)) - .collect() + .collect() } ``` From 118124d86ca09559f2c7f3ddf73b0704067f1358 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 17 Jan 2017 22:33:58 -0500 Subject: [PATCH 09/18] Fix spelling, grammar, and other small stuff --- dictionary.txt | 5 +++ src/ch13-00-functional-features.md | 57 ++++++++++++++++-------------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/dictionary.txt b/dictionary.txt index 99716d79b8..e67625b618 100644 --- a/dictionary.txt +++ b/dictionary.txt @@ -2,6 +2,7 @@ personal_ws-1.1 en 0 utf-8 abcabcabc abcd abcdefghijklmnopqrstuvwxyz +adaptor Addr aliasability alignof @@ -84,7 +85,10 @@ filename Filename filesystem Filesystem +FnMut +FnOnce formatter +FromIterator GitHub gitignore grapheme @@ -111,6 +115,7 @@ indices init instantiation internet +IntoIterator InvalidDigit ioerror iokind diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 6a35de76ef..f3db833ebe 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -23,8 +23,8 @@ code. ## Closures Rust gives you the ability to define *closures*, which are similar to -functions. Instead of starting with a technical definintion, let's see what -clousures look like, syntactically, and then we'll return to defining what they +functions. Instead of starting with a technical definition, let's see what +closures look like, syntactically, and then we'll return to defining what they are. Listing 13-1 shows a small closure whose definition is assigned to the variable `add_one`, which we can then use to call the closure: @@ -194,8 +194,8 @@ scope Here, even though `x` is not one of the parameters of `equal_to_x`, the `equal_to_x` closure is allowed to use `x`, since `x` is a variable defined in -the same scope that `equal_to_x` is defined. We aren't allowed to do the same -thing that Listing 13-4 does with functions; let's see what happens if we try: +the scope that `equal_to_x` is defined. We aren't allowed to do the same thing +that Listing 13-4 does with functions; let's see what happens if we try: Filename: src/main.rs @@ -554,7 +554,7 @@ So, to recap: 3. Use the `collect` adaptor to consume the iterator and make a new vector. That's how we end up with `[2, 3, 4]`. As you can see, closures are a very -important part of using iterators; they provide the way of customizing the +important part of using iterators; they provide a way of customizing the behavior of an iterator adapter like `map`. ### Iterators are Lazy @@ -627,7 +627,7 @@ impl Counter { } ``` -The `new` method here isn't strictly neccesary, but we want our `Counter` +The `new` method here isn't strictly necessary, but we want our `Counter` to go from one to five, so we're going to initialize it with a zero. Let's see why by implementing `Iterator` for it: @@ -704,7 +704,7 @@ fn main() { ``` At the time, we didn't explain the `.iter()` bit, but now you know that it -makes an iterator. Rust's `for` loop is actually 'synatx sugar', that is, it's +makes an iterator. Rust's `for` loop is actually 'syntax sugar', that is, it's special syntax, but we could write it ourselves. It's just a bit nicer to write by using `for`. If we took the code above and expanded it, it would look like this: @@ -734,12 +734,12 @@ let result = match IntoIterator::into_iter(a) { ``` `IntoIterator` is another trait that we haven't discussed yet. As the name -suggests, it has an `into_iter` method that takes one argument, and turns -that argument into an `Iterator`. This means that we can pass anything -that's can be converted into an iterator to a `for` loop, and it will -just work. That's nice! However, arrays do not implement `IntoIterator`, -and so we had to call the `iter` method ourself. But since that returns -an iterator, calling `into_iter` on it does nothing, so we're still good! +suggests, it has an `into_iter` method that takes one argument, and turns that +argument into an `Iterator`. This means that we can pass anything that can be +converted into an iterator to a `for` loop, and it will just work. That's nice! +However, arrays do not implement `IntoIterator`, and so we had to call the +`iter` method ourselves. But since that returns an iterator, calling `into_iter` +on it does nothing, so we're still good! We're also `match`ing on the iterator that's returned. Let's look at how that works: @@ -765,7 +765,7 @@ If we got `None` back from the iterator, we `break` out of the loop. ### IntoIterator and vectors -Let's talk a bit more about `IntoIterator`. As we said above, it's job is to +Let's talk a bit more about `IntoIterator`. As we said above, its job is to convert something into an iterator. You'll find it implemented on all kinds of handy things. Consider this example: @@ -789,7 +789,7 @@ for e in &mut v { } ``` -Whoah! The standard library implements `IntoIterator` on vectors directly, +Cool! The standard library implements `IntoIterator` on vectors directly, allowing you to take ownership of each element of the vector. But it also implements it on `&Vec` and `&mut Vec`, which allow you to iterate over references and mutable references, respectively. Since the `for` loop @@ -844,7 +844,7 @@ that iterator, and add the two numbers together. We then filter out only the sums that are less than 100, and then finally, take the first five of those numbers. Finally, `sum` will add up all of the numbers into one last number. This is kind of a silly calculation, but it shows off a few different iterator -adaptors, you can do all kinds of things! Check the documentation of `Iterator` +adaptors. You can do all kinds of things! Check the documentation of `Iterator` to see them all. Some crates you may use in the ecosystem might add even more adaptors as well. @@ -913,17 +913,17 @@ us the next value of the iterator. Let's use that: impl Config { fn new(mut args: std::env::Args) -> Result { // The first argument is the program name, let's ignore that - args.next(); + args.next(); let search = match args.next() { Some(arg) => arg, None => return "Didn't get a search string", - }; + }; let filename = match args.next() { Some(arg) => arg, None => return "Didn't get a file name", - }; + }; Ok(Config { search: search, @@ -983,7 +983,7 @@ fn grep_case_insensitive<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { } ``` -Not too bad! So which style should you chose? Most Rust programmers prefer to +Not too bad! So which style should you choose? Most Rust programmers prefer to use the iterator style. It's a bit tougher to understand at first, but once you gain an intuition for what the various iterator adaptors do, this is much easier to understand. Instead of fiddling with the various bits of looping @@ -992,7 +992,7 @@ and building a new vector, it focuses on the high-level objective of the loop. But are they truly equivalent? Surely the more low-level loop will be faster? Let's talk about performance. -## Summary: Performance +## Performance Which version of our `grep` is faster, the one with an explicit `for` loop, or iterators? We ran a quick benchmark by loading the entire contents of @@ -1005,7 +1005,7 @@ test bench_grep_iter ... bench: 19,234,900 ns/iter (+/- 657,200) ``` That's right, the iterator version ended up slightly faster! We're not going -to share the bencharmark code exactly here, as the point is not to prove that +to share the benchmark code exactly here, as the point is not to prove that they're exactly equivalent. For a _real_ benchmark, you'd want to check various texts of various sizes, different words, words of different lengths, and all kinds of other things. The point here is this: iterators, while a high-level @@ -1035,12 +1035,15 @@ from residues," if that means anything to you. The point is, doing math is something that often needs to be done very quickly, so you care about speed. But here, we're creating an iterator, using two adaptors, and then finally consuming the value. What would this code compile to? Well, as of this writing, -this, it compiles down to the same assembly you'd write by hand: there's no -loop at all, as it knows that there are twelve iterations, and so it "unrolls" -the loop. All of the coefficients get stored in registers (read: they're very -fast). There are no bounds checks on the array access. It's extremely -efficient. +it compiles down to the same assembly you'd write by hand: there's no loop at +all, as it knows that there are twelve iterations, and so it "unrolls" the +loop. All of the coefficients get stored in registers (read: they're very +fast). There are no bounds checks on the array access. It's extremely efficient. Now that you know this, go use iterators and closures without fear! They can really make code feel more high-level, but don't have a performance penalty for doing so. + +## Summary + +TODO \ No newline at end of file From a37a6bd78d4242efb8bf20c594f5c737faf59b96 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 18 Jan 2017 19:11:38 -0500 Subject: [PATCH 10/18] Add another spelling and ignoring --- dictionary.txt | 1 + src/ch13-00-functional-features.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dictionary.txt b/dictionary.txt index e67625b618..56ee463ab8 100644 --- a/dictionary.txt +++ b/dictionary.txt @@ -3,6 +3,7 @@ abcabcabc abcd abcdefghijklmnopqrstuvwxyz adaptor +adaptors Addr aliasability alignof diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index f3db833ebe..067e2aafb8 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -136,7 +136,7 @@ to call the same closure with an `i32`: Filename: src/main.rs -```rust +```rust,ignore let add_one = |x| x + 1; let five = add_one(4i8); From 117f3d8ecf7433e73bc668a25465e47bcafde58a Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Sun, 22 Jan 2017 15:09:55 -0500 Subject: [PATCH 11/18] More edits --- ...2-04-testing-the-librarys-functionality.md | 10 +- ...2-05-working-with-environment-variables.md | 11 +- ...-06-writing-to-stderr-instead-of-stdout.md | 4 +- src/ch13-00-functional-features.md | 609 ++++++++---------- 4 files changed, 307 insertions(+), 327 deletions(-) diff --git a/src/ch12-04-testing-the-librarys-functionality.md b/src/ch12-04-testing-the-librarys-functionality.md index bd1f10fae9..6cca79171d 100644 --- a/src/ch12-04-testing-the-librarys-functionality.md +++ b/src/ch12-04-testing-the-librarys-functionality.md @@ -188,8 +188,9 @@ fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { Finally, we need a way to store the lines that contain our search string. For that, we can make a mutable vector before the `for` loop and call the `push` method to store a `line` in the vector. After the `for` loop, we return the -vector: +vector. Listing 12-15 has the full implementation: +
Filename: src/lib.rs ```rust @@ -206,6 +207,13 @@ fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { } ``` +
+ +Listing 12-15: Fully functioning implementation of the `grep` function + +
+
+ Let's give it a try: diff --git a/src/ch12-05-working-with-environment-variables.md b/src/ch12-05-working-with-environment-variables.md index 8dab306d1f..7dc5ff9be8 100644 --- a/src/ch12-05-working-with-environment-variables.md +++ b/src/ch12-05-working-with-environment-variables.md @@ -52,8 +52,9 @@ Trust me."; We're going to define a new function named `grep_case_insensitive`. Its implementation will be almost the same as the `grep` function, but with some -minor changes: +minor changes as shown in Listing 12-16: +
Filename: src/lib.rs ```rust @@ -71,6 +72,14 @@ fn grep_case_insensitive<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { } ``` +
+ +Listing 12-16: Implementing a `grep_case_insensitive` function by changing the +search string and the lines of the contents to lowercase before comparing them + +
+
+ First, we lowercase the `search` string, and store it in a shadowed variable diff --git a/src/ch12-06-writing-to-stderr-instead-of-stdout.md b/src/ch12-06-writing-to-stderr-instead-of-stdout.md index e12d5416dd..b68964faf9 100644 --- a/src/ch12-06-writing-to-stderr-instead-of-stdout.md +++ b/src/ch12-06-writing-to-stderr-instead-of-stdout.md @@ -24,7 +24,7 @@ Problem parsing arguments: not enough arguments We'd like this to be printed to the screen instead, and only have the output from a successful run end up in the file if we run our program this way. Let's -change how error messages are printed as shown in Listing 12-15: +change how error messages are printed as shown in Listing 12-17:
Filename: src/main.rs @@ -67,7 +67,7 @@ fn main() {
-Listing 12-15: Writing error messages to `stderr` instead of `stdout` +Listing 12-17: Writing error messages to `stderr` instead of `stdout`
diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 067e2aafb8..251e14711c 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -164,7 +164,7 @@ they have different behavior than functions: closures possess an *environment*. ### Closures Can Reference Their Environment We've learned that functions can only use variables that are in scope, either -by being static or being declared as parameters. Closures can do more: they're +by being `const` or being declared as parameters. Closures can do more: they're allowed to access variables from their enclosing scope. Listing 13-4 has an example of a closure in the variable `equal_to_x` that uses the variable `x` from the closure's surrounding environment: @@ -496,15 +496,19 @@ ownership. Using `Fn` specifies that the closure used may only borrow values in its environment. To specify a closure that mutates the environment, use `FnMut`, and if the closure takes ownership of the environment, `FnOnce`. Most of the time, you can start with `Fn`, and the compiler will tell you if you -need `FnMut` or `FnOnce` based on the closure values passed into the function. +need `FnMut` or `FnOnce` based on what happens when the function calls the +closure. To illustrate a situation where it's useful for a function to have a parameter that's a closure, let's move on to our next topic: iterators. ## Iterators -Let's move on to another topic: iterators. Iterators are a pattern in Rust -that allows you to do some processing on a sequence of items. Like this: +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 +number in a vector: + +
```rust let v1 = vec![1, 2, 3]; @@ -514,55 +518,46 @@ let v2: Vec = v1.iter().map(|x| x + 1).collect(); assert_eq!(v2, [2, 3, 4]); ``` -That second line is full of new concepts. Let's check each bit out, in turn: - -```rust,ignore -v1.iter() -``` - -The `iter` method on vectors allows us to produce an *iterator* from the -vector. This iterator has a number of its own methods. The next section -is one of those: +
-```rust,ignore -.map(|x| x + 1) -``` +Listing 13-8: Using an iterator, `map`, and `collect` to add one to each number +in a vector -The `map` method on an iterator allows us to process each element: for every -element `x`, we add one to it. `map` is one of the most basic ways of -interacting with an iterator, as processing each element in turn is very -useful! +
+
-`map` itself is sometimes called an *iterator adapter*, that is, it's a method -on an iterator that produces a new iterator. That is, `map` builds on top of -our previous iterator and produces another one, by calling the closure it's -passed to produce the new sequence of values. There are many useful iterator -adapters, but before we talk about them, let's get to the last bit: + -```rust,ignore -.collect() -``` +The `iter` method on vectors allows us to produce an *iterator* from the +vector. Nex, the `map` method called on the iterator allows us to process each +element: in this case, we've passed a closure to `map` that specifies for every +element `x`, add one to it. `map` is one of the most basic ways of interacting +with an iterator, as processing each element in turn is very useful! Finally, +the `collect` method consumes the iterator and puts the iterator's elements +into a new data structure. In this case, since we've said that `v2` has the +type `Vec`, `collect` will create a new vector out of the `i32` values. -The `collect` method consumes an iterator, and puts them into a new data -structure. In this case, since we've said that `v2` has the type `Vec`, it -will create a new vector out of them. +Methods on iterators like `map` are sometimes called *iterator adaptors* +because they take one iterator and produce a new iterator. That is, `map` +builds on top of our previous iterator and produces another iterator by calling +the closure it's passed to create the new sequence of values. -So, to recap: +So, to recap, this line of code does the following: -1. Create an iterator from the vector. -2. Use the `map` adaptor to add one to each element. -3. Use the `collect` adaptor to consume the iterator and make a new vector. +1. Creates an iterator from the vector. +2. Uses the `map` adaptor with a closure argument to add one to each element. +3. Uses the `collect` adaptor to consume the iterator and make a new vector. That's how we end up with `[2, 3, 4]`. As you can see, closures are a very -important part of using iterators; they provide a way of customizing the -behavior of an iterator adapter like `map`. +important part of using iterators: they provide a way of customizing the +behavior of an iterator adaptor like `map`. ### Iterators are Lazy -In the previous section, you may have noticed a subtle bit of wording: we -said that `map` adapts an iterator, but that `collect` 'consumes' one. That -was intentional. By itself, iterators won't do anything; that is, they're -lazy. That is, if we write code like this: +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`: ```rust let v1: Vec = vec![1, 2, 3]; @@ -573,27 +568,26 @@ v1.iter().map(|x| x + 1); // without collect It will compile, but it will give us a warning: ```text -error: unused result which must be used: iterator adaptors are lazy and do - nothing unless consumed - --> :5:1 +warning: unused result which must be used: iterator adaptors are lazy and do +nothing unless consumed, #[warn(unused_must_use)] on by default + --> src/main.rs:4:1 | -5 | v1.iter().map(|x| x + 1); +4 | v1.iter().map(|x| x + 1); // without collect | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | ``` -That is, iterator adaptors won't start actually doing the processing on their -own. They need some sort of adaptor that causes the iterator chain to evaluate. -We call those 'consuming adaptors', and `collect` is one of them. +We get this warning because iterator adaptors won't start actually doing the +processing on their own. They need some other method that causes the iterator +chain to evaluate. We call those *consuming adaptors*, and `collect` is one of +them. -So, with these different kinds of iterator adaptors... how do we tell which -ones consume or not? And what adaptors are available? For that, let's look at -the `Iterator` trait. +So how do we tell which iterator methods consume the iterator or not? And what +adaptors are available? For that, let's look at the `Iterator` trait. -### The Iterator trait +### The `Iterator` trait -Iterators all implement a trait, `Iterator`, that is defined in the standard -library. It looks like this: +Iterators all implement a trait named `Iterator` that is defined in the standard +library. The definition of the trait looks like this: ```rust trait Iterator { @@ -603,21 +597,24 @@ trait Iterator { } ``` -There's some new syntax that we haven't covered here yet, and that's the `type -Item` and `Self::Item` bits. This is called an "associated type", and we'll -talk about them in Chapter XX. For now, all you need to know is that this -says that the `Iterator` trait requires that you also define an `Item` type, -and that its type is used in the return type of `next`. The `Item` type will -be the type of element that's returned from the iterator. You'll learn more -about why this is needed in that chapter. +There's some new syntax that we haven't covered here yet: `type Item` and +`Self::Item` are defining an *associated type* with this trait, and we'll talk +about associated types in depth in Chapter XX. For now, all you need to know is +that this code says the `Iterator` trait requires that you also define an +`Item` type, and this `Item` type is used in the return type of the `next` +method. In other words, the `Item` type will be the type of element that's +returned from the iterator. -Let's make an iterator named `Counter` which counts from `1` to `5`, using -this trait. First, we need to create a struct that holds the state for -our iterator: +Let's make an iterator named `Counter` that will count from `1` to `5`, using +the `Iterator` trait. First, we need to create a struct that holds the current +state of the iterator, which is one field named `count` that will hold a `u32`. +We'll also define a `new` method, which isn't strictly necessary. We want our +`Counter` to go from one to five, though, so we're always going to have it +holding a zero to start: ```rust struct Counter { - count: usize, + count: u32, } impl Counter { @@ -627,247 +624,131 @@ impl Counter { } ``` -The `new` method here isn't strictly necessary, but we want our `Counter` -to go from one to five, so we're going to initialize it with a zero. Let's -see why by implementing `Iterator` for it: +Next, we're going to implement the `Iterator` trait for our `Counter` type by +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`: -```rust,ignore +
+ +```rust +# struct Counter { +# count: u32, +# } +# impl Iterator for Counter { // Our iterator will produce u32s type Item = u32; fn next(&mut self) -> Option { - // increment our count. This is why we started at zero. - self.count += 1; - // check to see if we've finished counting or not. if self.count < 6 { + // if we're not done, go to the next value and return it. + self.count += 1; Some(self.count) } else { + // if we are done counting, return None. None } } } ``` -There's a lot going on here! First of all, we assign `Item` to be `u32`. Remember -that we don't really understand this syntax yet, you'll just have to trust me. - -Next, we implement the `next` method. This method is the main interface into an -iterator: it returns an option. If the option is `Some(value)`, we have gotten -another value from the iterator. If it's `None`, we know that iteration is -finished. Inside of it, we do whatever kind of calculation our iterator needs. -In this case, we add one, and then check to see if we're still below six. If we -are, we can return `Some(self.count)`, to give the next value, but if we're at -six or more, iteration is over, and we return `None`. - -Once we've implemented this, we have an iterator! We can use it by calling -`next` repeatedly: - -```rust,ignore -let mut counter = Counter::new(); - -let x = counter.next().unwrap(); -println!("{}", x); - -let x = counter.next().unwrap(); -println!("{}", x); - -let x = counter.next().unwrap(); -println!("{}", x); - -let x = counter.next().unwrap(); -println!("{}", x); - -let x = counter.next().unwrap(); -println!("{}", x); -``` - -This will print `1` through `5`, each on their own line. - -Calling `next()` this way gets repetitive. Rust has a construct which can call -`next()` on your iterator, until it reaches `None`. Let's go over that next. - -### IntoIterator and for loops - -When we said that iterators are important to Rust, we weren't joking: iterators -are how `for` loops work under the hood! Remember this `for` loop from Chapter 3? - -```rust -fn main() { - let a = [10, 20, 30, 40, 50]; - - for element in a.iter() { - println!("the value is: {}", element); - } -} -``` - -At the time, we didn't explain the `.iter()` bit, but now you know that it -makes an iterator. Rust's `for` loop is actually 'syntax sugar', that is, it's -special syntax, but we could write it ourselves. It's just a bit nicer to write -by using `for`. If we took the code above and expanded it, it would look like -this: - - -```rust,ignore -let a = [10, 20, 30, 40, 50]; - -{ - let result = match IntoIterator::into_iter(a) { - mut iter => loop { - match iter.next() { - Some(element) => { println!("the value is: {}", element); }, - None => break, - } - }, - }; - result -} -``` +
-Whew! This code is very compact, and uses a lot of concepts. We've talked -about them all already though, so let's break it down: +Listing 13-9: Implementing the `Iterator` trait on our `Counter` struct -```rust,ignore -let result = match IntoIterator::into_iter(a) { -``` +
+
-`IntoIterator` is another trait that we haven't discussed yet. As the name -suggests, it has an `into_iter` method that takes one argument, and turns that -argument into an `Iterator`. This means that we can pass anything that can be -converted into an iterator to a `for` loop, and it will just work. That's nice! -However, arrays do not implement `IntoIterator`, and so we had to call the -`iter` method ourselves. But since that returns an iterator, calling `into_iter` -on it does nothing, so we're still good! + -We're also `match`ing on the iterator that's returned. Let's look at how -that works: +The `type Item = u32` line is saying that the associated `Item` type will be +a `u32` for our iterator. Again, don't worry about associated types yet, because +we'll be covering them in Chapter XX. -```rust,ignore -mut iter => loop { -``` +The `next` method is the main interface into an iterator, and it returns an +`Option`. If the option is `Some(value)`, we have gotten another value from the +iterator. If it's `None`, iteration is finished. Inside of the `next` method, +we do whatever kind of calculation our iterator needs to do. In this case, we +add one, then check to see if we're still below six. If we are, we can return +`Some(self.count)` to produce the next value. If we're at six or more, +iteration is over, so we return `None`. -The `match` only has one arm: we match the result, binding it to a mutable -binding, `iter`, and then immediately call `loop` to loop forever. What's -in the body of that loop? Another `match!` +Once we've implemented the `Iterator` trait, we have an iterator! We can use +the iterator functionality that our `Counter` struct now has by calling the +`next` method on it repeatedly: ```rust,ignore -match iter.next() { - Some(element) => { println!("the value is: {}", element); }, - None => break, -} -``` - -Here, we call `next()`, and see if it's `Some`. If it is, then we call -the body of the `for` loop, which in this case is a single `println!`. -If we got `None` back from the iterator, we `break` out of the loop. - -### IntoIterator and vectors - -Let's talk a bit more about `IntoIterator`. As we said above, its job is to -convert something into an iterator. You'll find it implemented on all kinds of -handy things. Consider this example: - -```rust -let v = vec![1, 2, 3]; +let mut counter = Counter::new(); -for e in v { - println!("iterating by owner"); -} +let x = counter.next(); +println!("{:?}", x); -let v = vec![1, 2, 3]; +let x = counter.next(); +println!("{:?}", x); -for e in &v { - println!("iterating by reference"); -} +let x = counter.next(); +println!("{:?}", x); -let mut v = vec![1, 2, 3]; +let x = counter.next(); +println!("{:?}", x); -for e in &mut v { - println!("iterating by mutable reference"); -} +let x = counter.next(); +println!("{:?}", x); ``` -Cool! The standard library implements `IntoIterator` on vectors directly, -allowing you to take ownership of each element of the vector. But it also -implements it on `&Vec` and `&mut Vec`, which allow you to iterate over -references and mutable references, respectively. Since the `for` loop -implicitly calls `IntoIterator::into_iter` for us, we don't need to do -anything. It just works. - -`IntoIterator` is a very useful trait, but we should move on. You can find more -about it in its documentation. - -### All sorts of adaptors +This will print `1` through `5`, each on their own line. -So we implemented `Iterator` for our `Counter`, but we only wrote `next`. -When we used iterators at the start of this section, we had other methods, -like `map` and `collect`. What about those? +### All Sorts of `Iterator` Adaptors -Well, when we told you about the definition of `Iterator`, we committed a -small lie of omission. The `Iterator` trait has a number of other useful -methods. Like `map`: +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` +method on our `Counter`. How do we get methods like `map` and `collect` on our +`Counter`? -```rust,ignore -fn map(self, f: F) -> Map where - Self: Sized, F: FnMut(Self::Item) -> B; -``` +Well, when we told you about the definition of `Iterator`, we committed a small +lie of omission. The `Iterator` trait has a number of other useful methods +defined on it that come with default implementations that call the `next` +method. Since `next` is the only method of the `Iterator` trait that does not +have a default implementation, once you've done that, you get all of the other +`Iterator` adaptors for free. There are a lot of them! -And `collect`: +For example, if for some reason we wanted to take the first five values that +an instance of `Counter` produces, pair those values with values produced by +another `Counter` instance after skipping the first value that instance +produces, multiply each pair together, keep only those results that are +divisible by three, and add all the resulting values together, we could do: ```rust,ignore -fn collect>(self) -> B where Self: Sized; -``` - -Both of these type signatures make advanced usages of generics, so don't -worry about fully understanding them right now. The point is this: all -of these other methods on `Iterators` are implemented for you, in terms -of `next`. That is, you only need to implement `next`, and you get -all of the other adaptors for free. There are a lot of them! - -```rust -let sum: u64 = (1..).zip(2..) - .map(|(a, b)| a + b) - .filter(|&x| x < 100) - .take(5) - .sum(); - -assert_eq!(35, sum); +let sum: u32 = Counter::new().take(5) + .zip(Counter::new().skip(1)) + .map(|(a, b)| a * b) + .filter(|x| x % 3 == 0) + .sum(); +assert_eq!(48, sum); ``` -The `1..` and `2..` are ranges, but they work as infinite iterators: they'll -count from one and two until infinity. The `zip` adaptor zips two iterators -together, producing a new iterator which gives you tuples. The first element is -from the first iterator, the second is from the second iterator. We then take -that iterator, and add the two numbers together. We then filter out only the -sums that are less than 100, and then finally, take the first five of those -numbers. Finally, `sum` will add up all of the numbers into one last number. -This is kind of a silly calculation, but it shows off a few different iterator -adaptors. You can do all kinds of things! Check the documentation of `Iterator` -to see them all. Some crates you may use in the ecosystem might add even more -adaptors as well. +All of these method calls are possible because we implemented the `Iterator` +trait by specifying how the `next` method works. Use the standard library +documentation to find more useful methods that will come in handy when you're +working with iterators. -## Improving our I/O project +## Improving our I/O Project -Let's use what we learned to improve our project from the last chapter. Back -in listing 12-5, we had this code: - -```rust,ignore -fn parse_config(args: &[String]) -> Config { - let search = args[1].clone(); - let filename = args[2].clone(); +In our I/O project implementing `grep` in the last chapter, there are some +places where the code could be made clearer and more concise using iterators. +Let's take a look at how iterators can improve our implementation of the +`Config::new` function and the `grep` function. - Config { - search: search, - filename: filename, - } -} -``` +### Removing a `clone` by Using an Iterator -At the time, we told you to not worry about the `clone` calls here, and that -we could remove them in the future. Well, that time is now! Later in that -section, we moved this code into `Config::new`, and it looked like this: +Back in listing 12-8, we had this code that took a slice of `String` values and +created an instance of the `Config` struct by checking for the right number of +arguments, indexing into the slice, and cloning the values so that the `Config` +struct could own those values: ```rust,ignore impl Config { @@ -887,42 +768,70 @@ impl Config { } ``` -Let's fix that version, as it's the final form of the code we had. So, why do -we need `clone` here? The issue is, we have a slice of `String`s in `args`, -that is, we don't own them. So the only thing we can do is copy them. But now -that we know more about iterators, we can make this work. Let's modify -`new` to take a different kind of argument: +At the time, we said not to worry about the `clone` calls here, and that we +could remove them in the future. Well, that time is now! So, why do we need +`clone` here? The issue is that we have a slice with `String` elements in the +parameter `args`, and the `new` function does not own `args`. In order to be +able to return ownership of a `Config` instance, we need to clone the values +that we put in the `search` and `filename` fields of `Config`, so that the +`Config` instance can own its values. -```rust,ignore -fn new(args: std::env::Args) -> Result { -``` +Now that we know more about iterators, we can change the `new` function to +instead take ownership of an iterator as its argument. We'll use the iterator +functionality instead of having to check the length of the slice and index into +specific locations. Since we've taken ownership of the iterator, and we won't be +using indexing operations that borrow anymore, we can move the `String` values +from the iterator into `Config` instead of calling `clone` and making a new +allocation. -`std::env::Args` is the return type of `the `std::env::args` function. -It's an iterator! Let's modify `main` to pass it in directly, rather than -using `collect` to make a vector: +First, let's take `main` as it was in Listing 12-6, and change it to pass the +return value of `env::args` to `Config::new`, instead of calling `collect` and +passing a slice: ```rust,ignore fn main() { let config = Config::new(env::args()); + // ...snip... ``` -Much simpler. Now we need to fix the body. We know that `next` will give -us the next value of the iterator. Let's use that: + + +If we look in the standard library documentation for the `env::args` function, +we'll see that its return type is `std::env::Args`. So next we'll update the +signature of the `Config::new` function so that the parameter `args` has the +type `std::env::Args` instead of `&[String]`: + ```rust,ignore +impl Config { + fn new(args: std::env::Args) -> Result { + // ...snip... +``` + + + +Next, we'll fix the body of `Config::new`. As we can also see in the standard +library documentation, `std::env::Args` implements the `Iterator` trait, so we +know we can call the `next` method on it! Here's the new code: + +```rust +# struct Config { +# search: String, +# filename: String, +# } +# impl Config { fn new(mut args: std::env::Args) -> Result { - // The first argument is the program name, let's ignore that args.next(); let search = match args.next() { Some(arg) => arg, - None => return "Didn't get a search string", + None => return Err("Didn't get a search string"), }; let filename = match args.next() { Some(arg) => arg, - None => return "Didn't get a file name", + None => return Err("Didn't get a file name"), }; Ok(Config { @@ -933,16 +842,31 @@ impl Config { } ``` -Here, we use `next` to get the arguments out of `Args`. This code is way -better: we now will not `panic!` when we get too few arguments, but instead -return a `Result` with a meaningful error message. One slightly -unfortunate bit is the repetition with the `match`. `?` only works on -`Result`s, and not `Option`s at the moment. It also won't copy -the `String`s, as we can get them directly from the iterator, rather than -the slice we had before. Given that `Args` produces its arguments by value, -we move them instead. Another win! + + +Remember that the first value in the return value of `env::args` is the name of +the program. We want to ignore that, so first we'll call `next` and not do +anything with the return value. The second time we call `next` should be the +value we want to put in the `search` field of `Config`. We use a `match` to +extract the value if `next` returns a `Some`, and we return early with an `Err` +value if there weren't enough arguments (which would cause this call to `next` +to return `None`). + +We do the same thing for the `filename` value. It's slightly unfortunate that +the `match` expressions for `search` and `filename` are so similar. It would be +nice if we could use `?` on the `Option` returned from `next`, but `?` only +works with `Result` values currently. Even if we could use `?` on `Option` like +we can on `Result`, the value we would get would be borrowed, and we want to +move the `String` from the iterator into `Config`. + +### Making Code Clearer with Iterator Adaptors -The other bit was in our version of `grep`: +The other bit of code where we could take advantage of iterators was in the +`grep` function as implemented in Listing 12-15: + + ```rust fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { @@ -958,7 +882,9 @@ fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { } ``` -We can write this code like this instead: +We can write this code in a much shorter way, and avoiding having to have a +mutable intermediate `results` vector, by using iterator adaptor methods like +this instead: ```rust fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { @@ -968,11 +894,15 @@ fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { } ``` -Wow, much shorter! Here, we use the `filter` adapter to only select -the lines that `contains(search)` returns true for. We then collect -them up into another vector with `collect`. Much simpler! +Here, we use the `filter` adaptor to only keep the lines that +`line.contains(search)` returns true for. We then collect them up into another +vector with `collect`. Much simpler! + +We can use the same technique in the `grep_case_insensitive` function that we +defined in Listing 12-16 as follows: -We can do the same for `grep_case_insensitive`: + ```rust fn grep_case_insensitive<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { @@ -987,35 +917,55 @@ Not too bad! So which style should you choose? Most Rust programmers prefer to use the iterator style. It's a bit tougher to understand at first, but once you gain an intuition for what the various iterator adaptors do, this is much easier to understand. Instead of fiddling with the various bits of looping -and building a new vector, it focuses on the high-level objective of the loop. +and building a new vector, the code focuses on the high-level objective of the +loop, abstracting some of the commonplace code so that it's easier to see the +concepts that are unique to this usage of the code, like the condition on which +the code is filtering each element in the iterator. -But are they truly equivalent? Surely the more low-level loop will be faster? +But are they truly equivalent? Surely the more low-level loop will be faster. Let's talk about performance. ## Performance -Which version of our `grep` is faster, the one with an explicit `for` loop, -or iterators? We ran a quick benchmark by loading the entire contents of -"The Adventures of Sherlock Holmes" by Sir Arthur Conan Doyle into a `String` -and looking for the word `the` in it. Here were the times: +Which version of our `grep` functions is faster: the version with an explicit +`for` loop or the version with iterators? We ran a benchmark by loading the +entire contents of "The Adventures of Sherlock Holmes" by Sir Arthur Conan +Doyle into a `String` and looking for the word "the" in the contents. Here were +the results of the benchmark on the version of grep using the `for` loop and the +version using iterators: ```text test bench_grep_for ... bench: 19,620,300 ns/iter (+/- 915,700) test bench_grep_iter ... bench: 19,234,900 ns/iter (+/- 657,200) ``` -That's right, the iterator version ended up slightly faster! We're not going -to share the benchmark code exactly here, as the point is not to prove that -they're exactly equivalent. For a _real_ benchmark, you'd want to check various -texts of various sizes, different words, words of different lengths, and all -kinds of other things. The point here is this: iterators, while a high-level -abstraction, get compiled down to roughly the same code as if you'd written -the lower-level code yourself. - -As another example, here's an iterator chain that does some math: +The iterator version ended up slightly faster! We're not going to go through +the benchmark code here, as the point is not to prove that they're exactly +equivalent, but to get a general sense of how these two implementations +compare. For a *real* benchmark, you'd want to check various texts of various +sizes, different words, words of different lengths, and all kinds of other +variations. The point is this: iterators, while a high-level abstraction, get +compiled down to roughly the same code as if you'd written the lower-level code +yourself. Iterators are one of Rust's *zero-cost abstractions*, by which we mean +using the abstraction imposes no additional runtime overhead in the same way +that Bjarne Stroustrup, the original designer and implementer of C++, defines +*zero-overhead*: + +> In general, C++ implementations obey the zero-overhead principle: What you +> don’t use, you don’t pay for. And further: What you do use, you couldn’t hand +> code any better. +> +> - Bjarne Stroustrup "Foundations of C++" + +As another example, here is some code taken from an audio decoder. This code +uses an iterator chain to do some math on three variables in scope: a `buffer` +slice of data, an array of 12 `coefficients`, and an amount by which to shift +data in `qlp_shift`. We've declared the variables within this example but not +given them any values; while this code doesn't have much meaning outside of its +context, it's still a concise, real-world example of how Rust translates +high-level ideas to low-level code: ```rust,ignore -// We have these three variables in scope: let buffer: &mut [i32]; let coefficients: [i64; 12]; let qlp_shift: i16; @@ -1030,20 +980,33 @@ for i in 12..buffer.len() { } ``` -This code sample is taken from an audio decoder. It "restores sample values -from residues," if that means anything to you. The point is, doing math is -something that often needs to be done very quickly, so you care about speed. -But here, we're creating an iterator, using two adaptors, and then finally -consuming the value. What would this code compile to? Well, as of this writing, -it compiles down to the same assembly you'd write by hand: there's no loop at -all, as it knows that there are twelve iterations, and so it "unrolls" the -loop. All of the coefficients get stored in registers (read: they're very -fast). There are no bounds checks on the array access. It's extremely efficient. - -Now that you know this, go use iterators and closures without fear! They can -really make code feel more high-level, but don't have a performance penalty for +In order to calculate the value of `prediction`, this code iterates through +each of the 12 values in `coefficients`, uses the `zip` method to pair the +coefficient values with the previous 12 values in `buffer`. Then for each pair, +multiply the values together, sum all the results, and shift the bits in the +sum `qlp_shift` bits to the right + +Calculations in applications like audio decoders often prioritize performance +most highly. Here, we're creating an iterator, using two adaptors, then +consuming the value. What assembly code would this Rust code compile to? Well, +as of this writing, it compiles down to the same assembly you'd write by hand. +There's no loop at all corresponding to the iteration over the values in +`coefficients`: Rust knows that there are twelve iterations, so it "unrolls" +the loop. All of the coefficients get stored in registers (which means +accessing the values is very fast). There are no bounds checks on the array +access. It's extremely efficient. + +Now that you know this, go use iterators and closures without fear! They make +code feel higher-level, but don't impose a runtime performance penalty for doing so. ## Summary -TODO \ No newline at end of file +Closures and iterators are Rust features inspired by functional programming +language ideas. They contribute to Rust's ability to clearly express high-level +ideas. The implementations of closures and iterators, as well as other zero-cost +abstractions in Rust, are such that runtime performance is not affected. + +Now that we've improved the expressiveness of our I/O project, let's look at +some more features of `cargo` that would help us get ready to share the project +with the world. From e3a1744c92d9e5edfe6f08cfbce20b442aa6f04b Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Sun, 22 Jan 2017 16:05:21 -0500 Subject: [PATCH 12/18] Clarify what happens when iteration is done --- src/ch13-00-functional-features.md | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 251e14711c..025b5ce33e 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -643,13 +643,13 @@ impl Iterator for Counter { type Item = u32; fn next(&mut self) -> Option { + // increment our count. This is why we started at zero. + self.count += 1; + // check to see if we've finished counting or not. if self.count < 6 { - // if we're not done, go to the next value and return it. - self.count += 1; Some(self.count) } else { - // if we are done counting, return None. None } } @@ -677,6 +677,20 @@ add one, then check to see if we're still below six. If we are, we can return `Some(self.count)` to produce the next value. If we're at six or more, iteration is over, so we return `None`. +The iterator trait specifies that when an iterator returns `None`, that +indicates iteration is finished. The trait does not mandate anything about the +behavior an iterator must have if the `next` method is called again after +having returned one `None` value. In this case, every time we call `next` after +getting the first `None` value will still return `None`, but the internal +`count` field will continue to be incremented by one each time. If we call +`next` as many times as the maximum value a `u32` value can hold, `count` will +oveflow (which will `panic!` in debug mode and wrap in release mode). Other +iterator implementations choose to start iterating again. If you need to be +sure to have an iterator that will always return `None` on subsequent calls to +the `next` method after the first `None` value is returned, you can use the +`fuse` method to create an iterator with that characteristic out of any other +iterator. + Once we've implemented the `Iterator` trait, we have an iterator! We can use the iterator functionality that our `Counter` struct now has by calling the `next` method on it repeatedly: @@ -696,11 +710,15 @@ println!("{:?}", x); let x = counter.next(); println!("{:?}", x); +let x = counter.next(); +println!("{:?}", x); + let x = counter.next(); println!("{:?}", x); ``` -This will print `1` through `5`, each on their own line. +This will print `Some(1)` through `Some(5)` and then `None`, each on their own +line. ### All Sorts of `Iterator` Adaptors From fa861f78cccbff232441bb8d0f59f84523b434c2 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Sun, 22 Jan 2017 16:09:03 -0500 Subject: [PATCH 13/18] Add more words to the dictionary --- dictionary.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dictionary.txt b/dictionary.txt index 56ee463ab8..575badfe1f 100644 --- a/dictionary.txt +++ b/dictionary.txt @@ -24,6 +24,7 @@ bitwise Bitwise bitxor BitXor +Bjarne Boehm bool boolean @@ -127,6 +128,7 @@ IpAddrKind irst isize iter +iterator's JavaScript lang latin @@ -220,6 +222,7 @@ Stdin stdlib stdout steveklabnik's +Stroustrup struct Struct structs From 851bd9f108234ebc9269381b256436ea36beef57 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Sun, 22 Jan 2017 16:13:30 -0500 Subject: [PATCH 14/18] Break into sections --- src/SUMMARY.md | 6 +- src/ch13-00-functional-features.md | 1009 ----------------------- src/ch13-01-closures.md | 481 +++++++++++ src/ch13-02-iterators.md | 251 ++++++ src/ch13-03-improving-our-io-project.md | 188 +++++ src/ch13-04-performance.md | 85 ++ 6 files changed, 1010 insertions(+), 1010 deletions(-) create mode 100644 src/ch13-01-closures.md create mode 100644 src/ch13-02-iterators.md create mode 100644 src/ch13-03-improving-our-io-project.md create mode 100644 src/ch13-04-performance.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a2f4d6d76c..a6fd1c0f58 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -65,7 +65,11 @@ ## Thinking in Rust -- [Functional Language Features in Rust - Iterators and Closures](ch13-00-functional-features.md) +- [Functional Language Features in Rust](ch13-00-functional-features.md) + - [Closures](ch13-01-closures.md) + - [Iterators](ch13-02-iterators.md) + - [Improving our I/O Project](ch13-03-improving-our-io-project.md) + - [Performance](ch13-04-performance.md) - [More about Cargo and Crates.io](ch14-00-more-about-cargo.md) - [Release Profiles](ch14-01-release-profiles.md) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index 025b5ce33e..c1e8f2c62c 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -19,1012 +19,3 @@ This is not a complete list of Rust's influence from the functional style: pattern matching, enums, and many other features are too. But mastering closures and iterators are an important part of writing idiomatic, fast Rust code. - -## Closures - -Rust gives you the ability to define *closures*, which are similar to -functions. Instead of starting with a technical definition, let's see what -closures look like, syntactically, and then we'll return to defining what they -are. Listing 13-1 shows a small closure whose definition is assigned to the -variable `add_one`, which we can then use to call the closure: - -
-Filename: src/main.rs - -```rust -fn main() { - let add_one = |x| x + 1; - - let five = add_one(4); - - assert_eq!(5, five); -} -``` - -
- -Listing 13-1: A closure that takes one parameter and adds one to it, assigned to -the variable `add_one` - -
-
- -The closure definition, on the first line, shows that the closure takes one -parameter named `x`. Parameters to closures go in between vertical pipes (`|`). - -This is a minimal closure with only one expression as its body. Listing 13-2 has -a closure with a bit more complexity: - -
-Filename: src/main.rs - -```rust -fn main() { - let calculate = |a, b| { - let mut result = a * 2; - - result += b; - - result - }; - - assert_eq!(7, calculate(2, 3)); // 2 * 2 + 3 == 7 - assert_eq!(13, calculate(4, 5)); // 4 * 2 + 5 == 13 -} -``` - -
- -Listing 13-2: A closure with two parameters and multiple expressions in its body - -
-
- -We can use curly brackets to define a closure body with more than one -expression. - -You'll notice a few things about closures that are different from functions -defined with the `fn` keyword. The first difference is that we did not need to -annotate the types of the parameters the closure takes or the value it returns. -We can choose to add type annotations if we want; Listing 13-3 shows the -closure from Listing 13-1 with annotations for the parameter's and return -value's types: - -
-Filename: src/main.rs - -```rust -fn main() { - let add_one = |x: i32| -> i32 { x + 1 }; - - assert_eq!(2, add_one(1)); -} -``` - -
- -Listing 13-3: A closure definition with optional parameter and return value -type annotations - -
-
- -The syntax of closures and functions looks more similar with type annotations. -Let's compare the different ways we can specify closures with the syntax for -defining a function more directly. We've added some spaces here to line up the -relevant parts: - -```rust -fn add_one_v1 (x: i32) -> i32 { x + 1 } // a function -let add_one_v2 = |x: i32| -> i32 { x + 1 }; // the full syntax for a closure -let add_one_v3 = |x: i32| { x + 1 }; // a closure eliding types -let add_one_v4 = |x: i32| x + 1 ; // without braces -``` - -The reason type annotations are not required for defining a closure but are -required for defining a function is that functions are part of an explicit -interface exposed to your users, so defining this interface rigidly is -important for ensuring that everyone agrees on what types of values a function -uses and returns. Closures aren't used in an exposed interface like this, -though: they're stored in bindings and called directly. Being forced to -annotate the types would be a significant ergonomic loss for little advantage. - -Closure definitions do have one type inferred for each of their parameters and -for their return value. For instance, if we call the closure without type -annotations from Listing 13-1 using an `i8`, we'll get an error if we then try -to call the same closure with an `i32`: - -Filename: src/main.rs - -```rust,ignore -let add_one = |x| x + 1; - -let five = add_one(4i8); -assert_eq!(5i8, five); - -let three = add_one(2i32); -``` - -The compiler gives us this error: - -```text -error[E0308]: mismatched types - --> - | -7 | let three = add_one(2i32); - | ^^^^ expected i8, found i32 -``` - -Since closures' types can be inferred reliably since they're called directly, -it would be tedious if we were required to annotate their types. - -Another reason to have a different syntax from functions for closures is that -they have different behavior than functions: closures possess an *environment*. - -### Closures Can Reference Their Environment - -We've learned that functions can only use variables that are in scope, either -by being `const` or being declared as parameters. Closures can do more: they're -allowed to access variables from their enclosing scope. Listing 13-4 has an -example of a closure in the variable `equal_to_x` that uses the variable `x` -from the closure's surrounding environment: - -
-Filename: src/main.rs - -```rust -fn main() { - let x = 4; - - let equal_to_x = |z| z == x; - - let y = 4; - - assert!(equal_to_x(y)); -} -``` - -
- -Listing 13-4: Example of a closure that refers to a variable in its enclosing -scope - -
-
- -Here, even though `x` is not one of the parameters of `equal_to_x`, the -`equal_to_x` closure is allowed to use `x`, since `x` is a variable defined in -the scope that `equal_to_x` is defined. We aren't allowed to do the same thing -that Listing 13-4 does with functions; let's see what happens if we try: - -Filename: src/main.rs - -```rust,ignore -fn main() { - let x = 4; - - fn equal_to_x(z: i32) -> bool { z == x } - - let y = 4; - - assert!(equal_to_x(y)); -} -``` - -We get an error: - -```text -error[E0434]: can't capture dynamic environment in a fn item; use the || { ... } -closure form instead - --> - | -4 | fn equal_to_x(z: i32) -> bool { z == x } - | ^ -``` - -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`. - -### Closures as Function Parameters Using the `Fn` Traits - -While we can bind closures to variables, that's not the most useful thing we -can do with them. We can also define functions that have closures as parameters -by using the `Fn` traits. Here's an example of a function named `call_with_one` -whose signature has a closure as a parameter: - -```rust -fn call_with_one(some_closure: F) -> i32 - where F: Fn(i32) -> i32 { - - some_closure(1) -} - -let answer = call_with_one(|x| x + 2); - -assert_eq!(3, answer); -``` - -We pass the closure `|x| x + 2`, to `call_with_one`, and `call_with_one` calls -that closure with `1` as an argument. The return value of the call to -`some_closure` is then returned from `call_with_one`. - -The signature of `call_with_one` is using the `where` syntax discussed in the -Traits section of Chapter 10. The `some_closure` parameter has the generic type -`F`, which in the `where` clause is defined as having the trait bounds -`Fn(i32) -> i32`. The `Fn` trait represents a closure, and we can add types to -the `Fn` trait to represent a specific type of closure. In this case, our -closure has a parameter of type `i32` and returns an `i32`, so the generic bound -we specify is `Fn(i32) -> i32`. - -Specifying a function signature that contains a closure requires the use of -generics and trait bounds. Each closure has a unique type, so we can't write -the type of a closure directly, we have to use generics. - -`Fn` isn't the only trait bound available for specifying closures, however. -There are three: `Fn`, `FnMut`, and `FnOnce`. This continues the patterns of -threes we've seen elsewhere in Rust: borrowing, borrowing mutably, and -ownership. Using `Fn` specifies that the closure used may only borrow values in -its environment. To specify a closure that mutates the environment, use -`FnMut`, and if the closure takes ownership of the environment, `FnOnce`. Most -of the time, you can start with `Fn`, and the compiler will tell you if you -need `FnMut` or `FnOnce` based on what happens when the function calls the -closure. - -To illustrate a situation where it's useful for a function to have a parameter -that's a closure, let's move on to our next topic: iterators. - -## 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 -number in a vector: - -
- -```rust -let v1 = vec![1, 2, 3]; - -let v2: Vec = v1.iter().map(|x| x + 1).collect(); - -assert_eq!(v2, [2, 3, 4]); -``` - -
- -Listing 13-8: Using an iterator, `map`, and `collect` to add one to each number -in a vector - -
-
- - - -The `iter` method on vectors allows us to produce an *iterator* from the -vector. Nex, the `map` method called on the iterator allows us to process each -element: in this case, we've passed a closure to `map` that specifies for every -element `x`, add one to it. `map` is one of the most basic ways of interacting -with an iterator, as processing each element in turn is very useful! Finally, -the `collect` method consumes the iterator and puts the iterator's elements -into a new data structure. In this case, since we've said that `v2` has the -type `Vec`, `collect` will create a new vector out of the `i32` values. - -Methods on iterators like `map` are sometimes called *iterator adaptors* -because they take one iterator and produce a new iterator. That is, `map` -builds on top of our previous iterator and produces another iterator by calling -the closure it's passed to create the new sequence of values. - -So, to recap, this line of code does the following: - -1. Creates an iterator from the vector. -2. Uses the `map` adaptor with a closure argument to add one to each element. -3. Uses the `collect` adaptor to consume the iterator and make a new vector. - -That's how we end up with `[2, 3, 4]`. As you can see, closures are a very -important part of using iterators: they provide a way of customizing the -behavior of an iterator adaptor like `map`. - -### Iterators are Lazy - -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`: - -```rust -let v1: Vec = vec![1, 2, 3]; - -v1.iter().map(|x| x + 1); // without collect -``` - -It will compile, but it will give us a warning: - -```text -warning: unused result which must be used: iterator adaptors are lazy and do -nothing unless consumed, #[warn(unused_must_use)] on by default - --> src/main.rs:4:1 - | -4 | v1.iter().map(|x| x + 1); // without collect - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -``` - -We get this warning because iterator adaptors won't start actually doing the -processing on their own. They need some other method that causes the iterator -chain to evaluate. We call those *consuming adaptors*, and `collect` is one of -them. - -So how do we tell which iterator methods consume the iterator or not? And what -adaptors are available? For that, let's look at the `Iterator` trait. - -### The `Iterator` trait - -Iterators all implement a trait named `Iterator` that is defined in the standard -library. The definition of the trait looks like this: - -```rust -trait Iterator { - type Item; - - fn next(&mut self) -> Option; -} -``` - -There's some new syntax that we haven't covered here yet: `type Item` and -`Self::Item` are defining an *associated type* with this trait, and we'll talk -about associated types in depth in Chapter XX. For now, all you need to know is -that this code says the `Iterator` trait requires that you also define an -`Item` type, and this `Item` type is used in the return type of the `next` -method. In other words, the `Item` type will be the type of element that's -returned from the iterator. - -Let's make an iterator named `Counter` that will count from `1` to `5`, using -the `Iterator` trait. First, we need to create a struct that holds the current -state of the iterator, which is one field named `count` that will hold a `u32`. -We'll also define a `new` method, which isn't strictly necessary. We want our -`Counter` to go from one to five, though, so we're always going to have it -holding a zero to start: - -```rust -struct Counter { - count: u32, -} - -impl Counter { - fn new() -> Counter { - Counter { count: 0 } - } -} -``` - -Next, we're going to implement the `Iterator` trait for our `Counter` type by -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`: - -
- -```rust -# struct Counter { -# count: u32, -# } -# -impl Iterator for Counter { - // Our iterator will produce u32s - type Item = u32; - - fn next(&mut self) -> Option { - // increment our count. This is why we started at zero. - self.count += 1; - - // check to see if we've finished counting or not. - if self.count < 6 { - Some(self.count) - } else { - None - } - } -} -``` - -
- -Listing 13-9: Implementing the `Iterator` trait on our `Counter` struct - -
-
- - - -The `type Item = u32` line is saying that the associated `Item` type will be -a `u32` for our iterator. Again, don't worry about associated types yet, because -we'll be covering them in Chapter XX. - -The `next` method is the main interface into an iterator, and it returns an -`Option`. If the option is `Some(value)`, we have gotten another value from the -iterator. If it's `None`, iteration is finished. Inside of the `next` method, -we do whatever kind of calculation our iterator needs to do. In this case, we -add one, then check to see if we're still below six. If we are, we can return -`Some(self.count)` to produce the next value. If we're at six or more, -iteration is over, so we return `None`. - -The iterator trait specifies that when an iterator returns `None`, that -indicates iteration is finished. The trait does not mandate anything about the -behavior an iterator must have if the `next` method is called again after -having returned one `None` value. In this case, every time we call `next` after -getting the first `None` value will still return `None`, but the internal -`count` field will continue to be incremented by one each time. If we call -`next` as many times as the maximum value a `u32` value can hold, `count` will -oveflow (which will `panic!` in debug mode and wrap in release mode). Other -iterator implementations choose to start iterating again. If you need to be -sure to have an iterator that will always return `None` on subsequent calls to -the `next` method after the first `None` value is returned, you can use the -`fuse` method to create an iterator with that characteristic out of any other -iterator. - -Once we've implemented the `Iterator` trait, we have an iterator! We can use -the iterator functionality that our `Counter` struct now has by calling the -`next` method on it repeatedly: - -```rust,ignore -let mut counter = Counter::new(); - -let x = counter.next(); -println!("{:?}", x); - -let x = counter.next(); -println!("{:?}", x); - -let x = counter.next(); -println!("{:?}", x); - -let x = counter.next(); -println!("{:?}", x); - -let x = counter.next(); -println!("{:?}", x); - -let x = counter.next(); -println!("{:?}", x); -``` - -This will print `Some(1)` through `Some(5)` and then `None`, each on their own -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` -method on our `Counter`. How do we get methods like `map` and `collect` on our -`Counter`? - -Well, when we told you about the definition of `Iterator`, we committed a small -lie of omission. The `Iterator` trait has a number of other useful methods -defined on it that come with default implementations that call the `next` -method. Since `next` is the only method of the `Iterator` trait that does not -have a default implementation, once you've done that, you get all of the other -`Iterator` adaptors for free. There are a lot of them! - -For example, if for some reason we wanted to take the first five values that -an instance of `Counter` produces, pair those values with values produced by -another `Counter` instance after skipping the first value that instance -produces, multiply each pair together, keep only those results that are -divisible by three, and add all the resulting values together, we could do: - -```rust,ignore -let sum: u32 = Counter::new().take(5) - .zip(Counter::new().skip(1)) - .map(|(a, b)| a * b) - .filter(|x| x % 3 == 0) - .sum(); -assert_eq!(48, sum); -``` - -All of these method calls are possible because we implemented the `Iterator` -trait by specifying how the `next` method works. Use the standard library -documentation to find more useful methods that will come in handy when you're -working with iterators. - -## Improving our I/O Project - -In our I/O project implementing `grep` in the last chapter, there are some -places where the code could be made clearer and more concise using iterators. -Let's take a look at how iterators can improve our implementation of the -`Config::new` function and the `grep` function. - -### Removing a `clone` by Using an Iterator - -Back in listing 12-8, we had this code that took a slice of `String` values and -created an instance of the `Config` struct by checking for the right number of -arguments, indexing into the slice, and cloning the values so that the `Config` -struct could own those values: - -```rust,ignore -impl Config { - fn new(args: &[String]) -> Result { - if args.len() < 3 { - return Err("not enough arguments"); - } - - let search = args[1].clone(); - let filename = args[2].clone(); - - Ok(Config { - search: search, - filename: filename, - }) - } -} -``` - -At the time, we said not to worry about the `clone` calls here, and that we -could remove them in the future. Well, that time is now! So, why do we need -`clone` here? The issue is that we have a slice with `String` elements in the -parameter `args`, and the `new` function does not own `args`. In order to be -able to return ownership of a `Config` instance, we need to clone the values -that we put in the `search` and `filename` fields of `Config`, so that the -`Config` instance can own its values. - -Now that we know more about iterators, we can change the `new` function to -instead take ownership of an iterator as its argument. We'll use the iterator -functionality instead of having to check the length of the slice and index into -specific locations. Since we've taken ownership of the iterator, and we won't be -using indexing operations that borrow anymore, we can move the `String` values -from the iterator into `Config` instead of calling `clone` and making a new -allocation. - -First, let's take `main` as it was in Listing 12-6, and change it to pass the -return value of `env::args` to `Config::new`, instead of calling `collect` and -passing a slice: - -```rust,ignore -fn main() { - let config = Config::new(env::args()); - // ...snip... -``` - - - -If we look in the standard library documentation for the `env::args` function, -we'll see that its return type is `std::env::Args`. So next we'll update the -signature of the `Config::new` function so that the parameter `args` has the -type `std::env::Args` instead of `&[String]`: - - -```rust,ignore -impl Config { - fn new(args: std::env::Args) -> Result { - // ...snip... -``` - - - -Next, we'll fix the body of `Config::new`. As we can also see in the standard -library documentation, `std::env::Args` implements the `Iterator` trait, so we -know we can call the `next` method on it! Here's the new code: - -```rust -# struct Config { -# search: String, -# filename: String, -# } -# -impl Config { - fn new(mut args: std::env::Args) -> Result { - args.next(); - - let search = match args.next() { - Some(arg) => arg, - None => return Err("Didn't get a search string"), - }; - - let filename = match args.next() { - Some(arg) => arg, - None => return Err("Didn't get a file name"), - }; - - Ok(Config { - search: search, - filename: filename, - }) - } -} -``` - - - -Remember that the first value in the return value of `env::args` is the name of -the program. We want to ignore that, so first we'll call `next` and not do -anything with the return value. The second time we call `next` should be the -value we want to put in the `search` field of `Config`. We use a `match` to -extract the value if `next` returns a `Some`, and we return early with an `Err` -value if there weren't enough arguments (which would cause this call to `next` -to return `None`). - -We do the same thing for the `filename` value. It's slightly unfortunate that -the `match` expressions for `search` and `filename` are so similar. It would be -nice if we could use `?` on the `Option` returned from `next`, but `?` only -works with `Result` values currently. Even if we could use `?` on `Option` like -we can on `Result`, the value we would get would be borrowed, and we want to -move the `String` from the iterator into `Config`. - -### Making Code Clearer with Iterator Adaptors - -The other bit of code where we could take advantage of iterators was in the -`grep` function as implemented in Listing 12-15: - - - -```rust -fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { - let mut results = Vec::new(); - - for line in contents.lines() { - if line.contains(search) { - results.push(line); - } - } - - results -} -``` - -We can write this code in a much shorter way, and avoiding having to have a -mutable intermediate `results` vector, by using iterator adaptor methods like -this instead: - -```rust -fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { - contents.lines() - .filter(|line| line.contains(search)) - .collect() -} -``` - -Here, we use the `filter` adaptor to only keep the lines that -`line.contains(search)` returns true for. We then collect them up into another -vector with `collect`. Much simpler! - -We can use the same technique in the `grep_case_insensitive` function that we -defined in Listing 12-16 as follows: - - - -```rust -fn grep_case_insensitive<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { - contents.lines() - .filter(|line| { - line.to_lowercase().contains(&search) - }).collect() -} -``` - -Not too bad! So which style should you choose? Most Rust programmers prefer to -use the iterator style. It's a bit tougher to understand at first, but once you -gain an intuition for what the various iterator adaptors do, this is much -easier to understand. Instead of fiddling with the various bits of looping -and building a new vector, the code focuses on the high-level objective of the -loop, abstracting some of the commonplace code so that it's easier to see the -concepts that are unique to this usage of the code, like the condition on which -the code is filtering each element in the iterator. - -But are they truly equivalent? Surely the more low-level loop will be faster. -Let's talk about performance. - -## Performance - -Which version of our `grep` functions is faster: the version with an explicit -`for` loop or the version with iterators? We ran a benchmark by loading the -entire contents of "The Adventures of Sherlock Holmes" by Sir Arthur Conan -Doyle into a `String` and looking for the word "the" in the contents. Here were -the results of the benchmark on the version of grep using the `for` loop and the -version using iterators: - -```text -test bench_grep_for ... bench: 19,620,300 ns/iter (+/- 915,700) -test bench_grep_iter ... bench: 19,234,900 ns/iter (+/- 657,200) -``` - -The iterator version ended up slightly faster! We're not going to go through -the benchmark code here, as the point is not to prove that they're exactly -equivalent, but to get a general sense of how these two implementations -compare. For a *real* benchmark, you'd want to check various texts of various -sizes, different words, words of different lengths, and all kinds of other -variations. The point is this: iterators, while a high-level abstraction, get -compiled down to roughly the same code as if you'd written the lower-level code -yourself. Iterators are one of Rust's *zero-cost abstractions*, by which we mean -using the abstraction imposes no additional runtime overhead in the same way -that Bjarne Stroustrup, the original designer and implementer of C++, defines -*zero-overhead*: - -> In general, C++ implementations obey the zero-overhead principle: What you -> don’t use, you don’t pay for. And further: What you do use, you couldn’t hand -> code any better. -> -> - Bjarne Stroustrup "Foundations of C++" - -As another example, here is some code taken from an audio decoder. This code -uses an iterator chain to do some math on three variables in scope: a `buffer` -slice of data, an array of 12 `coefficients`, and an amount by which to shift -data in `qlp_shift`. We've declared the variables within this example but not -given them any values; while this code doesn't have much meaning outside of its -context, it's still a concise, real-world example of how Rust translates -high-level ideas to low-level code: - -```rust,ignore -let buffer: &mut [i32]; -let coefficients: [i64; 12]; -let qlp_shift: i16; - -for i in 12..buffer.len() { - let prediction = coefficients.iter() - .zip(&buffer[i - 12..i]) - .map(|(&c, &s)| c * s as i64) - .sum::() >> qlp_shift; - let delta = buffer[i]; - buffer[i] = prediction as i32 + delta; -} -``` - -In order to calculate the value of `prediction`, this code iterates through -each of the 12 values in `coefficients`, uses the `zip` method to pair the -coefficient values with the previous 12 values in `buffer`. Then for each pair, -multiply the values together, sum all the results, and shift the bits in the -sum `qlp_shift` bits to the right - -Calculations in applications like audio decoders often prioritize performance -most highly. Here, we're creating an iterator, using two adaptors, then -consuming the value. What assembly code would this Rust code compile to? Well, -as of this writing, it compiles down to the same assembly you'd write by hand. -There's no loop at all corresponding to the iteration over the values in -`coefficients`: Rust knows that there are twelve iterations, so it "unrolls" -the loop. All of the coefficients get stored in registers (which means -accessing the values is very fast). There are no bounds checks on the array -access. It's extremely efficient. - -Now that you know this, go use iterators and closures without fear! They make -code feel higher-level, but don't impose a runtime performance penalty for -doing so. - -## Summary - -Closures and iterators are Rust features inspired by functional programming -language ideas. They contribute to Rust's ability to clearly express high-level -ideas. The implementations of closures and iterators, as well as other zero-cost -abstractions in Rust, are such that runtime performance is not affected. - -Now that we've improved the expressiveness of our I/O project, let's look at -some more features of `cargo` that would help us get ready to share the project -with the world. diff --git a/src/ch13-01-closures.md b/src/ch13-01-closures.md new file mode 100644 index 0000000000..ff1260ab1a --- /dev/null +++ b/src/ch13-01-closures.md @@ -0,0 +1,481 @@ +## Closures + +Rust gives you the ability to define *closures*, which are similar to +functions. Instead of starting with a technical definition, let's see what +closures look like, syntactically, and then we'll return to defining what they +are. Listing 13-1 shows a small closure whose definition is assigned to the +variable `add_one`, which we can then use to call the closure: + +
+Filename: src/main.rs + +```rust +fn main() { + let add_one = |x| x + 1; + + let five = add_one(4); + + assert_eq!(5, five); +} +``` + +
+ +Listing 13-1: A closure that takes one parameter and adds one to it, assigned to +the variable `add_one` + +
+
+ +The closure definition, on the first line, shows that the closure takes one +parameter named `x`. Parameters to closures go in between vertical pipes (`|`). + +This is a minimal closure with only one expression as its body. Listing 13-2 has +a closure with a bit more complexity: + +
+Filename: src/main.rs + +```rust +fn main() { + let calculate = |a, b| { + let mut result = a * 2; + + result += b; + + result + }; + + assert_eq!(7, calculate(2, 3)); // 2 * 2 + 3 == 7 + assert_eq!(13, calculate(4, 5)); // 4 * 2 + 5 == 13 +} +``` + +
+ +Listing 13-2: A closure with two parameters and multiple expressions in its body + +
+
+ +We can use curly brackets to define a closure body with more than one +expression. + +You'll notice a few things about closures that are different from functions +defined with the `fn` keyword. The first difference is that we did not need to +annotate the types of the parameters the closure takes or the value it returns. +We can choose to add type annotations if we want; Listing 13-3 shows the +closure from Listing 13-1 with annotations for the parameter's and return +value's types: + +
+Filename: src/main.rs + +```rust +fn main() { + let add_one = |x: i32| -> i32 { x + 1 }; + + assert_eq!(2, add_one(1)); +} +``` + +
+ +Listing 13-3: A closure definition with optional parameter and return value +type annotations + +
+
+ +The syntax of closures and functions looks more similar with type annotations. +Let's compare the different ways we can specify closures with the syntax for +defining a function more directly. We've added some spaces here to line up the +relevant parts: + +```rust +fn add_one_v1 (x: i32) -> i32 { x + 1 } // a function +let add_one_v2 = |x: i32| -> i32 { x + 1 }; // the full syntax for a closure +let add_one_v3 = |x: i32| { x + 1 }; // a closure eliding types +let add_one_v4 = |x: i32| x + 1 ; // without braces +``` + +The reason type annotations are not required for defining a closure but are +required for defining a function is that functions are part of an explicit +interface exposed to your users, so defining this interface rigidly is +important for ensuring that everyone agrees on what types of values a function +uses and returns. Closures aren't used in an exposed interface like this, +though: they're stored in bindings and called directly. Being forced to +annotate the types would be a significant ergonomic loss for little advantage. + +Closure definitions do have one type inferred for each of their parameters and +for their return value. For instance, if we call the closure without type +annotations from Listing 13-1 using an `i8`, we'll get an error if we then try +to call the same closure with an `i32`: + +Filename: src/main.rs + +```rust,ignore +let add_one = |x| x + 1; + +let five = add_one(4i8); +assert_eq!(5i8, five); + +let three = add_one(2i32); +``` + +The compiler gives us this error: + +```text +error[E0308]: mismatched types + --> + | +7 | let three = add_one(2i32); + | ^^^^ expected i8, found i32 +``` + +Since closures' types can be inferred reliably since they're called directly, +it would be tedious if we were required to annotate their types. + +Another reason to have a different syntax from functions for closures is that +they have different behavior than functions: closures possess an *environment*. + +### Closures Can Reference Their Environment + +We've learned that functions can only use variables that are in scope, either +by being `const` or being declared as parameters. Closures can do more: they're +allowed to access variables from their enclosing scope. Listing 13-4 has an +example of a closure in the variable `equal_to_x` that uses the variable `x` +from the closure's surrounding environment: + +
+Filename: src/main.rs + +```rust +fn main() { + let x = 4; + + let equal_to_x = |z| z == x; + + let y = 4; + + assert!(equal_to_x(y)); +} +``` + +
+ +Listing 13-4: Example of a closure that refers to a variable in its enclosing +scope + +
+
+ +Here, even though `x` is not one of the parameters of `equal_to_x`, the +`equal_to_x` closure is allowed to use `x`, since `x` is a variable defined in +the scope that `equal_to_x` is defined. We aren't allowed to do the same thing +that Listing 13-4 does with functions; let's see what happens if we try: + +Filename: src/main.rs + +```rust,ignore +fn main() { + let x = 4; + + fn equal_to_x(z: i32) -> bool { z == x } + + let y = 4; + + assert!(equal_to_x(y)); +} +``` + +We get an error: + +```text +error[E0434]: can't capture dynamic environment in a fn item; use the || { ... } +closure form instead + --> + | +4 | fn equal_to_x(z: i32) -> bool { z == x } + | ^ +``` + +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`. + +### Closures as Function Parameters Using the `Fn` Traits + +While we can bind closures to variables, that's not the most useful thing we +can do with them. We can also define functions that have closures as parameters +by using the `Fn` traits. Here's an example of a function named `call_with_one` +whose signature has a closure as a parameter: + +```rust +fn call_with_one(some_closure: F) -> i32 + where F: Fn(i32) -> i32 { + + some_closure(1) +} + +let answer = call_with_one(|x| x + 2); + +assert_eq!(3, answer); +``` + +We pass the closure `|x| x + 2`, to `call_with_one`, and `call_with_one` calls +that closure with `1` as an argument. The return value of the call to +`some_closure` is then returned from `call_with_one`. + +The signature of `call_with_one` is using the `where` syntax discussed in the +Traits section of Chapter 10. The `some_closure` parameter has the generic type +`F`, which in the `where` clause is defined as having the trait bounds +`Fn(i32) -> i32`. The `Fn` trait represents a closure, and we can add types to +the `Fn` trait to represent a specific type of closure. In this case, our +closure has a parameter of type `i32` and returns an `i32`, so the generic bound +we specify is `Fn(i32) -> i32`. + +Specifying a function signature that contains a closure requires the use of +generics and trait bounds. Each closure has a unique type, so we can't write +the type of a closure directly, we have to use generics. + +`Fn` isn't the only trait bound available for specifying closures, however. +There are three: `Fn`, `FnMut`, and `FnOnce`. This continues the patterns of +threes we've seen elsewhere in Rust: borrowing, borrowing mutably, and +ownership. Using `Fn` specifies that the closure used may only borrow values in +its environment. To specify a closure that mutates the environment, use +`FnMut`, and if the closure takes ownership of the environment, `FnOnce`. Most +of the time, you can start with `Fn`, and the compiler will tell you if you +need `FnMut` or `FnOnce` based on what happens when the function calls the +closure. + +To illustrate a situation where it's useful for a function to have a parameter +that's a closure, let's move on to our next topic: iterators. diff --git a/src/ch13-02-iterators.md b/src/ch13-02-iterators.md new file mode 100644 index 0000000000..adf65fca25 --- /dev/null +++ b/src/ch13-02-iterators.md @@ -0,0 +1,251 @@ +## 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 +number in a vector: + +
+ +```rust +let v1 = vec![1, 2, 3]; + +let v2: Vec = v1.iter().map(|x| x + 1).collect(); + +assert_eq!(v2, [2, 3, 4]); +``` + +
+ +Listing 13-8: Using an iterator, `map`, and `collect` to add one to each number +in a vector + +
+
+ + + +The `iter` method on vectors allows us to produce an *iterator* from the +vector. Nex, the `map` method called on the iterator allows us to process each +element: in this case, we've passed a closure to `map` that specifies for every +element `x`, add one to it. `map` is one of the most basic ways of interacting +with an iterator, as processing each element in turn is very useful! Finally, +the `collect` method consumes the iterator and puts the iterator's elements +into a new data structure. In this case, since we've said that `v2` has the +type `Vec`, `collect` will create a new vector out of the `i32` values. + +Methods on iterators like `map` are sometimes called *iterator adaptors* +because they take one iterator and produce a new iterator. That is, `map` +builds on top of our previous iterator and produces another iterator by calling +the closure it's passed to create the new sequence of values. + +So, to recap, this line of code does the following: + +1. Creates an iterator from the vector. +2. Uses the `map` adaptor with a closure argument to add one to each element. +3. Uses the `collect` adaptor to consume the iterator and make a new vector. + +That's how we end up with `[2, 3, 4]`. As you can see, closures are a very +important part of using iterators: they provide a way of customizing the +behavior of an iterator adaptor like `map`. + +### Iterators are Lazy + +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`: + +```rust +let v1: Vec = vec![1, 2, 3]; + +v1.iter().map(|x| x + 1); // without collect +``` + +It will compile, but it will give us a warning: + +```text +warning: unused result which must be used: iterator adaptors are lazy and do +nothing unless consumed, #[warn(unused_must_use)] on by default + --> src/main.rs:4:1 + | +4 | v1.iter().map(|x| x + 1); // without collect + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +``` + +We get this warning because iterator adaptors won't start actually doing the +processing on their own. They need some other method that causes the iterator +chain to evaluate. We call those *consuming adaptors*, and `collect` is one of +them. + +So how do we tell which iterator methods consume the iterator or not? And what +adaptors are available? For that, let's look at the `Iterator` trait. + +### The `Iterator` trait + +Iterators all implement a trait named `Iterator` that is defined in the standard +library. The definition of the trait looks like this: + +```rust +trait Iterator { + type Item; + + fn next(&mut self) -> Option; +} +``` + +There's some new syntax that we haven't covered here yet: `type Item` and +`Self::Item` are defining an *associated type* with this trait, and we'll talk +about associated types in depth in Chapter XX. For now, all you need to know is +that this code says the `Iterator` trait requires that you also define an +`Item` type, and this `Item` type is used in the return type of the `next` +method. In other words, the `Item` type will be the type of element that's +returned from the iterator. + +Let's make an iterator named `Counter` that will count from `1` to `5`, using +the `Iterator` trait. First, we need to create a struct that holds the current +state of the iterator, which is one field named `count` that will hold a `u32`. +We'll also define a `new` method, which isn't strictly necessary. We want our +`Counter` to go from one to five, though, so we're always going to have it +holding a zero to start: + +```rust +struct Counter { + count: u32, +} + +impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } +} +``` + +Next, we're going to implement the `Iterator` trait for our `Counter` type by +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`: + +
+ +```rust +# struct Counter { +# count: u32, +# } +# +impl Iterator for Counter { + // Our iterator will produce u32s + type Item = u32; + + fn next(&mut self) -> Option { + // increment our count. This is why we started at zero. + self.count += 1; + + // check to see if we've finished counting or not. + if self.count < 6 { + Some(self.count) + } else { + None + } + } +} +``` + +
+ +Listing 13-9: Implementing the `Iterator` trait on our `Counter` struct + +
+
+ + + +The `type Item = u32` line is saying that the associated `Item` type will be +a `u32` for our iterator. Again, don't worry about associated types yet, because +we'll be covering them in Chapter XX. + +The `next` method is the main interface into an iterator, and it returns an +`Option`. If the option is `Some(value)`, we have gotten another value from the +iterator. If it's `None`, iteration is finished. Inside of the `next` method, +we do whatever kind of calculation our iterator needs to do. In this case, we +add one, then check to see if we're still below six. If we are, we can return +`Some(self.count)` to produce the next value. If we're at six or more, +iteration is over, so we return `None`. + +The iterator trait specifies that when an iterator returns `None`, that +indicates iteration is finished. The trait does not mandate anything about the +behavior an iterator must have if the `next` method is called again after +having returned one `None` value. In this case, every time we call `next` after +getting the first `None` value will still return `None`, but the internal +`count` field will continue to be incremented by one each time. If we call +`next` as many times as the maximum value a `u32` value can hold, `count` will +oveflow (which will `panic!` in debug mode and wrap in release mode). Other +iterator implementations choose to start iterating again. If you need to be +sure to have an iterator that will always return `None` on subsequent calls to +the `next` method after the first `None` value is returned, you can use the +`fuse` method to create an iterator with that characteristic out of any other +iterator. + +Once we've implemented the `Iterator` trait, we have an iterator! We can use +the iterator functionality that our `Counter` struct now has by calling the +`next` method on it repeatedly: + +```rust,ignore +let mut counter = Counter::new(); + +let x = counter.next(); +println!("{:?}", x); + +let x = counter.next(); +println!("{:?}", x); + +let x = counter.next(); +println!("{:?}", x); + +let x = counter.next(); +println!("{:?}", x); + +let x = counter.next(); +println!("{:?}", x); + +let x = counter.next(); +println!("{:?}", x); +``` + +This will print `Some(1)` through `Some(5)` and then `None`, each on their own +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` +method on our `Counter`. How do we get methods like `map` and `collect` on our +`Counter`? + +Well, when we told you about the definition of `Iterator`, we committed a small +lie of omission. The `Iterator` trait has a number of other useful methods +defined on it that come with default implementations that call the `next` +method. Since `next` is the only method of the `Iterator` trait that does not +have a default implementation, once you've done that, you get all of the other +`Iterator` adaptors for free. There are a lot of them! + +For example, if for some reason we wanted to take the first five values that +an instance of `Counter` produces, pair those values with values produced by +another `Counter` instance after skipping the first value that instance +produces, multiply each pair together, keep only those results that are +divisible by three, and add all the resulting values together, we could do: + +```rust,ignore +let sum: u32 = Counter::new().take(5) + .zip(Counter::new().skip(1)) + .map(|(a, b)| a * b) + .filter(|x| x % 3 == 0) + .sum(); +assert_eq!(48, sum); +``` + +All of these method calls are possible because we implemented the `Iterator` +trait by specifying how the `next` method works. Use the standard library +documentation to find more useful methods that will come in handy when you're +working with iterators. diff --git a/src/ch13-03-improving-our-io-project.md b/src/ch13-03-improving-our-io-project.md new file mode 100644 index 0000000000..cdedaac2ca --- /dev/null +++ b/src/ch13-03-improving-our-io-project.md @@ -0,0 +1,188 @@ +## Improving our I/O Project + +In our I/O project implementing `grep` in the last chapter, there are some +places where the code could be made clearer and more concise using iterators. +Let's take a look at how iterators can improve our implementation of the +`Config::new` function and the `grep` function. + +### Removing a `clone` by Using an Iterator + +Back in listing 12-8, we had this code that took a slice of `String` values and +created an instance of the `Config` struct by checking for the right number of +arguments, indexing into the slice, and cloning the values so that the `Config` +struct could own those values: + +```rust,ignore +impl Config { + fn new(args: &[String]) -> Result { + if args.len() < 3 { + return Err("not enough arguments"); + } + + let search = args[1].clone(); + let filename = args[2].clone(); + + Ok(Config { + search: search, + filename: filename, + }) + } +} +``` + +At the time, we said not to worry about the `clone` calls here, and that we +could remove them in the future. Well, that time is now! So, why do we need +`clone` here? The issue is that we have a slice with `String` elements in the +parameter `args`, and the `new` function does not own `args`. In order to be +able to return ownership of a `Config` instance, we need to clone the values +that we put in the `search` and `filename` fields of `Config`, so that the +`Config` instance can own its values. + +Now that we know more about iterators, we can change the `new` function to +instead take ownership of an iterator as its argument. We'll use the iterator +functionality instead of having to check the length of the slice and index into +specific locations. Since we've taken ownership of the iterator, and we won't be +using indexing operations that borrow anymore, we can move the `String` values +from the iterator into `Config` instead of calling `clone` and making a new +allocation. + +First, let's take `main` as it was in Listing 12-6, and change it to pass the +return value of `env::args` to `Config::new`, instead of calling `collect` and +passing a slice: + +```rust,ignore +fn main() { + let config = Config::new(env::args()); + // ...snip... +``` + + + +If we look in the standard library documentation for the `env::args` function, +we'll see that its return type is `std::env::Args`. So next we'll update the +signature of the `Config::new` function so that the parameter `args` has the +type `std::env::Args` instead of `&[String]`: + + +```rust,ignore +impl Config { + fn new(args: std::env::Args) -> Result { + // ...snip... +``` + + + +Next, we'll fix the body of `Config::new`. As we can also see in the standard +library documentation, `std::env::Args` implements the `Iterator` trait, so we +know we can call the `next` method on it! Here's the new code: + +```rust +# struct Config { +# search: String, +# filename: String, +# } +# +impl Config { + fn new(mut args: std::env::Args) -> Result { + args.next(); + + let search = match args.next() { + Some(arg) => arg, + None => return Err("Didn't get a search string"), + }; + + let filename = match args.next() { + Some(arg) => arg, + None => return Err("Didn't get a file name"), + }; + + Ok(Config { + search: search, + filename: filename, + }) + } +} +``` + + + +Remember that the first value in the return value of `env::args` is the name of +the program. We want to ignore that, so first we'll call `next` and not do +anything with the return value. The second time we call `next` should be the +value we want to put in the `search` field of `Config`. We use a `match` to +extract the value if `next` returns a `Some`, and we return early with an `Err` +value if there weren't enough arguments (which would cause this call to `next` +to return `None`). + +We do the same thing for the `filename` value. It's slightly unfortunate that +the `match` expressions for `search` and `filename` are so similar. It would be +nice if we could use `?` on the `Option` returned from `next`, but `?` only +works with `Result` values currently. Even if we could use `?` on `Option` like +we can on `Result`, the value we would get would be borrowed, and we want to +move the `String` from the iterator into `Config`. + +### Making Code Clearer with Iterator Adaptors + +The other bit of code where we could take advantage of iterators was in the +`grep` function as implemented in Listing 12-15: + + + +```rust +fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { + let mut results = Vec::new(); + + for line in contents.lines() { + if line.contains(search) { + results.push(line); + } + } + + results +} +``` + +We can write this code in a much shorter way, and avoiding having to have a +mutable intermediate `results` vector, by using iterator adaptor methods like +this instead: + +```rust +fn grep<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { + contents.lines() + .filter(|line| line.contains(search)) + .collect() +} +``` + +Here, we use the `filter` adaptor to only keep the lines that +`line.contains(search)` returns true for. We then collect them up into another +vector with `collect`. Much simpler! + +We can use the same technique in the `grep_case_insensitive` function that we +defined in Listing 12-16 as follows: + + + +```rust +fn grep_case_insensitive<'a>(search: &str, contents: &'a str) -> Vec<&'a str> { + contents.lines() + .filter(|line| { + line.to_lowercase().contains(&search) + }).collect() +} +``` + +Not too bad! So which style should you choose? Most Rust programmers prefer to +use the iterator style. It's a bit tougher to understand at first, but once you +gain an intuition for what the various iterator adaptors do, this is much +easier to understand. Instead of fiddling with the various bits of looping +and building a new vector, the code focuses on the high-level objective of the +loop, abstracting some of the commonplace code so that it's easier to see the +concepts that are unique to this usage of the code, like the condition on which +the code is filtering each element in the iterator. + +But are they truly equivalent? Surely the more low-level loop will be faster. +Let's talk about performance. diff --git a/src/ch13-04-performance.md b/src/ch13-04-performance.md new file mode 100644 index 0000000000..358208956e --- /dev/null +++ b/src/ch13-04-performance.md @@ -0,0 +1,85 @@ +## Performance + +Which version of our `grep` functions is faster: the version with an explicit +`for` loop or the version with iterators? We ran a benchmark by loading the +entire contents of "The Adventures of Sherlock Holmes" by Sir Arthur Conan +Doyle into a `String` and looking for the word "the" in the contents. Here were +the results of the benchmark on the version of grep using the `for` loop and the +version using iterators: + +```text +test bench_grep_for ... bench: 19,620,300 ns/iter (+/- 915,700) +test bench_grep_iter ... bench: 19,234,900 ns/iter (+/- 657,200) +``` + +The iterator version ended up slightly faster! We're not going to go through +the benchmark code here, as the point is not to prove that they're exactly +equivalent, but to get a general sense of how these two implementations +compare. For a *real* benchmark, you'd want to check various texts of various +sizes, different words, words of different lengths, and all kinds of other +variations. The point is this: iterators, while a high-level abstraction, get +compiled down to roughly the same code as if you'd written the lower-level code +yourself. Iterators are one of Rust's *zero-cost abstractions*, by which we mean +using the abstraction imposes no additional runtime overhead in the same way +that Bjarne Stroustrup, the original designer and implementer of C++, defines +*zero-overhead*: + +> In general, C++ implementations obey the zero-overhead principle: What you +> don’t use, you don’t pay for. And further: What you do use, you couldn’t hand +> code any better. +> +> - Bjarne Stroustrup "Foundations of C++" + +As another example, here is some code taken from an audio decoder. This code +uses an iterator chain to do some math on three variables in scope: a `buffer` +slice of data, an array of 12 `coefficients`, and an amount by which to shift +data in `qlp_shift`. We've declared the variables within this example but not +given them any values; while this code doesn't have much meaning outside of its +context, it's still a concise, real-world example of how Rust translates +high-level ideas to low-level code: + +```rust,ignore +let buffer: &mut [i32]; +let coefficients: [i64; 12]; +let qlp_shift: i16; + +for i in 12..buffer.len() { + let prediction = coefficients.iter() + .zip(&buffer[i - 12..i]) + .map(|(&c, &s)| c * s as i64) + .sum::() >> qlp_shift; + let delta = buffer[i]; + buffer[i] = prediction as i32 + delta; +} +``` + +In order to calculate the value of `prediction`, this code iterates through +each of the 12 values in `coefficients`, uses the `zip` method to pair the +coefficient values with the previous 12 values in `buffer`. Then for each pair, +multiply the values together, sum all the results, and shift the bits in the +sum `qlp_shift` bits to the right + +Calculations in applications like audio decoders often prioritize performance +most highly. Here, we're creating an iterator, using two adaptors, then +consuming the value. What assembly code would this Rust code compile to? Well, +as of this writing, it compiles down to the same assembly you'd write by hand. +There's no loop at all corresponding to the iteration over the values in +`coefficients`: Rust knows that there are twelve iterations, so it "unrolls" +the loop. All of the coefficients get stored in registers (which means +accessing the values is very fast). There are no bounds checks on the array +access. It's extremely efficient. + +Now that you know this, go use iterators and closures without fear! They make +code feel higher-level, but don't impose a runtime performance penalty for +doing so. + +## Summary + +Closures and iterators are Rust features inspired by functional programming +language ideas. They contribute to Rust's ability to clearly express high-level +ideas. The implementations of closures and iterators, as well as other zero-cost +abstractions in Rust, are such that runtime performance is not affected. + +Now that we've improved the expressiveness of our I/O project, let's look at +some more features of `cargo` that would help us get ready to share the project +with the world. From 528c2c97d0177fd9c24142283f73184630086133 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Sun, 22 Jan 2017 21:17:31 -0500 Subject: [PATCH 15/18] Fix typos --- src/ch13-02-iterators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ch13-02-iterators.md b/src/ch13-02-iterators.md index adf65fca25..35a6655b1e 100644 --- a/src/ch13-02-iterators.md +++ b/src/ch13-02-iterators.md @@ -25,7 +25,7 @@ in a vector The `iter` method on vectors allows us to produce an *iterator* from the -vector. Nex, the `map` method called on the iterator allows us to process each +vector. Next, the `map` method called on the iterator allows us to process each element: in this case, we've passed a closure to `map` that specifies for every element `x`, add one to it. `map` is one of the most basic ways of interacting with an iterator, as processing each element in turn is very useful! Finally, @@ -180,7 +180,7 @@ having returned one `None` value. In this case, every time we call `next` after getting the first `None` value will still return `None`, but the internal `count` field will continue to be incremented by one each time. If we call `next` as many times as the maximum value a `u32` value can hold, `count` will -oveflow (which will `panic!` in debug mode and wrap in release mode). Other +overflow (which will `panic!` in debug mode and wrap in release mode). Other iterator implementations choose to start iterating again. If you need to be sure to have an iterator that will always return `None` on subsequent calls to the `next` method after the first `None` value is returned, you can use the From f033a6fe98dcf3cf1bb26a28ef0639b20dbc9b46 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 23 Jan 2017 14:34:53 -0500 Subject: [PATCH 16/18] Fixing steve nits --- src/ch13-00-functional-features.md | 2 +- src/ch13-01-closures.md | 4 ++-- src/ch13-03-improving-our-io-project.md | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ch13-00-functional-features.md b/src/ch13-00-functional-features.md index c1e8f2c62c..55cce67706 100644 --- a/src/ch13-00-functional-features.md +++ b/src/ch13-00-functional-features.md @@ -3,7 +3,7 @@ Rust's design has taken inspiration from a lot of previous work. One of Rust's influences is functional programming, where functions are values that can be used as arguments or return values to other functions, assigned to variables, -and so forth. We're going to sidestep the issue of what, exactly, function +and so forth. We're going to sidestep the issue of what, exactly, functional programming is or is not, and instead show off some features of Rust that are similar to features in many languages referred to as functional. diff --git a/src/ch13-01-closures.md b/src/ch13-01-closures.md index ff1260ab1a..2f2b7f9382 100644 --- a/src/ch13-01-closures.md +++ b/src/ch13-01-closures.md @@ -95,8 +95,8 @@ relevant parts: ```rust fn add_one_v1 (x: i32) -> i32 { x + 1 } // a function let add_one_v2 = |x: i32| -> i32 { x + 1 }; // the full syntax for a closure -let add_one_v3 = |x: i32| { x + 1 }; // a closure eliding types -let add_one_v4 = |x: i32| x + 1 ; // without braces +let add_one_v3 = |x| { x + 1 }; // a closure eliding types +let add_one_v4 = |x| x + 1 ; // without braces ``` The reason type annotations are not required for defining a closure but are diff --git a/src/ch13-03-improving-our-io-project.md b/src/ch13-03-improving-our-io-project.md index cdedaac2ca..57e085b1f6 100644 --- a/src/ch13-03-improving-our-io-project.md +++ b/src/ch13-03-improving-our-io-project.md @@ -89,12 +89,12 @@ impl Config { let search = match args.next() { Some(arg) => arg, None => return Err("Didn't get a search string"), - }; + }; let filename = match args.next() { Some(arg) => arg, None => return Err("Didn't get a file name"), - }; + }; Ok(Config { search: search, From 231932a9281c15f79174873a487e79e00af44728 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 23 Jan 2017 15:17:13 -0500 Subject: [PATCH 17/18] Move (pun intended) closure env capture/borrowing/etc to concurrency --- src/ch13-01-closures.md | 233 +------------------------------------ src/ch13-02-iterators.md | 14 +-- src/ch16-00-concurrency.md | 232 ++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 236 deletions(-) diff --git a/src/ch13-01-closures.md b/src/ch13-01-closures.md index 2f2b7f9382..be9f8a665c 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 35a6655b1e..b7e4c03d31 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 c9603edd6c..9db08e0dd7 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 From 1a604211f28c0e3fe4641c929873d9bedf6264c5 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 23 Jan 2017 15:19:45 -0500 Subject: [PATCH 18/18] Ignore this test now --- src/ch13-01-closures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ch13-01-closures.md b/src/ch13-01-closures.md index be9f8a665c..082fdac4a9 100644 --- a/src/ch13-01-closures.md +++ b/src/ch13-01-closures.md @@ -92,7 +92,7 @@ Let's compare the different ways we can specify closures with the syntax for defining a function more directly. We've added some spaces here to line up the relevant parts: -```rust +```rust,ignore fn add_one_v1 (x: i32) -> i32 { x + 1 } // a function let add_one_v2 = |x: i32| -> i32 { x + 1 }; // the full syntax for a closure let add_one_v3 = |x| { x + 1 }; // a closure eliding types