X-Git-Url: https://git.ralfj.de/rust-101.git/blobdiff_plain/39b387735112972cad7bb3175393a0a09d767335..73bdc651abfbe484d9c97835efc4ad272c44d325:/src/part10.rs diff --git a/src/part10.rs b/src/part10.rs index cfa10e4..0c90369 100644 --- a/src/part10.rs +++ b/src/part10.rs @@ -9,10 +9,10 @@ use part05::BigInt; //@ our function. In Rust, a natural first attempt to express this is to have a trait for it. // So, let us define a trait that demands that the type provides some method `do_action` on digits. -//@ This immediately raises the question: How do we pass `self` to that function? Owned, shared borrow, -//@ or mutable borrow? The typical strategy to answer this question is to use the strongest +//@ This immediately raises the question: How do we pass `self` to that function? Owned, shared reference, +//@ or mutable reference? The typical strategy to answer this question is to use the strongest //@ type that still works. Certainly, passing `self` in owned form does not work: Then the function -//@ would consume `self`, and we could not call it again, on the second digit. So let's go with a mutable borrow. +//@ would consume `self`, and we could not call it again, on the second digit. So let's go with a mutable reference. trait Action { fn do_action(&mut self, digit: u64); } @@ -21,7 +21,7 @@ trait Action { impl BigInt { fn act_v1(&self, mut a: A) { //@ Remember that the `mut` above is just an annotation to Rust, telling it that we're okay with `a` being mutated. - //@ Calling `do_action` on `a` takes a mutable borrow, so mutation could indeed happen. + //@ Calling `do_action` on `a` takes a mutable reference, so mutation could indeed happen. for digit in self { a.do_action(digit); /*@*/ } @@ -36,7 +36,7 @@ struct PrintWithString { } impl Action for PrintWithString { - // Here we perform performs the actual printing of the prefix and the digit. We're not making use of our ability to + // Here we perform the actual printing of the prefix and the digit. We're not making use of our ability to // change `self` here, but we could replace the prefix if we wanted. fn do_action(&mut self, digit: u64) { println!("{}{}", self.prefix, digit); /*@*/ @@ -62,7 +62,7 @@ pub fn main() { //@ In general, this is called a *closure*. Closures take some arguments and produce a result, and they have an *environment* //@ they can use, which corresponds to the type `PrintWithString` (or any other type implementing `Action`). Again we have the //@ choice of passing this environment in owned or borrowed form, so there are three traits for closures in Rust: `Fn`-closures -//@ get a shared borrow, `FnMut`-closures get a mutable borrow, and `FnOnce`-closures consume their environment (and can hence +//@ get a shared reference, `FnMut`-closures get a mutable reference, and `FnOnce`-closures consume their environment (and can hence //@ be called only once). The syntax for a closure trait which takes arguments of type `T1`, `T2`, ... and returns something //@ of type `U` is `Fn(T1, T2, ...) -> U`. @@ -81,7 +81,7 @@ impl BigInt { pub fn print_with_prefix(b: &BigInt, prefix: String) { //@ The syntax for closures is `|arg1, arg2, ...| code`. Notice that the closure can reference variables like `prefix` that it did not //@ take as argument - variables that happen to be present *outside* of the closure. We say that the closure *captures* - //@ variables. Rust will now automatically create a type (like `PrintWithStruct`) for the environment of the closure + //@ variables. Rust will now automatically create a type (like `PrintWithString`) for the environment of the closure //@ with fields for every captured variable, implement the closure trait for this type such that the action performed //@ is given by the code of the closure, and finally it will instantiate the environment type here at the definition site //@ of the closure and fill it appropriately. @@ -94,11 +94,11 @@ pub fn print_with_prefix(b: &BigInt, prefix: String) { // For example, we can use that to count the digits as they are printed. pub fn print_and_count(b: &BigInt) { let mut count: usize = 0; - //@ This time, the environment will contain a field of type `&mut usize`, that will be initialized with a mutable borrow of + //@ This time, the environment will contain a field of type `&mut usize`, that will be initialized with a mutable reference of //@ `count`. The closure, since it mutably borrows its environment, is able to access this field and mutate `count` - //@ through it. Once `act` returns, the closure is destroyed and the borrow of `count` ends. Because closures compile down + //@ through it. Once `act` returns, the closure is destroyed and `count` is no longer borrowed. Because closures compile down //@ to normal types, all the borrow checking continues to work as usually, and we cannot accidentally leak a closure somewhere - //@ that still contains, in its environment, a borrow that has ended. + //@ that still contains, in its environment, a dead reference. b.act(|digit| { println!("{}: {}", count, digit); count = count +1; } ); println!("There are {} digits", count); } @@ -108,7 +108,7 @@ pub fn print_and_count(b: &BigInt) { //@ Rust provides a whole lot of methods on iterators that allow us to write pretty functional-style list manipulation. // Let's say we want to write a function that increments every entry of a `Vec` by some number, then looks for numbers larger than some threshold, and prints them. -fn inc_print_even(v: &Vec, offset: i32, threshold: i32) { +fn inc_print_threshold(v: &Vec, offset: i32, threshold: i32) { //@ `map` takes a closure that is applied to every element of the iterator. `filter` removes elements //@ from the iterator that do not pass the test given by the closure. //@ @@ -131,14 +131,14 @@ fn print_enumerated(v: &Vec) { // And as a final example, one can also collect all elements of an iterator, and put them, e.g., in a vector. fn filter_vec_by_divisor(v: &Vec, divisor: i32) -> Vec { //@ Here, the return type of `collect` is inferred based on the return type of our function. In general, it can return anything implementing - //@ [`FromIterator`](http://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html). Notice that `iter` gives us an iterator over + //@ [`FromIterator`](https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html). Notice that `iter` gives us an iterator over //@ borrowed `i32`, but we want to own them for the result, so we insert a `map` to dereference. v.iter().map(|n| *n).filter(|n| *n % divisor == 0).collect() /*@*/ } -// **Exercise 10.1**: Look up the [documentation of `Iterator`](http://doc.rust-lang.org/stable/std/iter/trait.Iterator.html) to learn about more functions +// **Exercise 10.1**: Look up the [documentation of `Iterator`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html) to learn about more functions // that can act on iterators. Try using some of them. What about a function that sums the even numbers of an iterator? Or a function that computes the // product of those numbers that sit at odd positions? A function that checks whether a vector contains a certain number? Whether all numbers are // smaller than some threshold? Be creative! -//@ [index](main.html) | [previous](part09.html) | [next](main.html) +//@ [index](main.html) | [previous](part09.html) | [raw source](workspace/src/part10.rs) | [next](part11.html)