terminology: &T and &mut T are the types of *references*. Also, stress the exclusivit...
authorRalf Jung <post@ralfj.de>
Sun, 17 Jan 2016 14:13:38 +0000 (15:13 +0100)
committerRalf Jung <post@ralfj.de>
Sun, 17 Jan 2016 14:13:38 +0000 (15:13 +0100)
21 files changed:
src/main.rs
src/part04.rs
src/part05.rs
src/part06.rs
src/part07.rs
src/part08.rs
src/part09.rs
src/part10.rs
src/part11.rs
src/part12.rs
src/part13.rs
src/part14.rs
src/part15.rs
src/part16.rs
workspace/src/part04.rs
workspace/src/part05.rs
workspace/src/part06.rs
workspace/src/part08.rs
workspace/src/part11.rs
workspace/src/part12.rs
workspace/src/part16.rs

index 8fbc933865a28e40266f67615fde82ef8b479ec4..9b7132d59862ca852ab558bd6f9a86bb90c00eec 100644 (file)
@@ -65,7 +65,7 @@
 // 
 // ### Basic Rust
 // 
-// * [Part 04: Ownership, Borrowing](part04.html)
+// * [Part 04: Ownership, Borrowing, References](part04.html)
 // * [Part 05: Clone](part05.html)
 // * [Part 06: Copy, Lifetimes](part06.html)
 // * [Part 07: Operator Overloading, Tests, Formating](part07.html)
index e7cf7f2b29042d5b22d52079c0d8cc8eb08a3c9c..fcfc8d92800cb2a54f592ff360a1b5d6e68dd11b 100644 (file)
@@ -1,5 +1,5 @@
-// Rust-101, Part 04: Ownership, Borrowing
-// =======================================
+// Rust-101, Part 04: Ownership, Borrowing, References
+// ===================================================
 
 //@ Rust aims to be a "safe systems language". As a systems language, of course it
 //@ provides *references* (or *pointers*). But as a safe language, it has to
@@ -43,33 +43,33 @@ fn ownership_demo() {
 //@ Essentially, ownership rules out aliasing, hence making the kind of problem discussed above
 //@ impossible.
 
-// ## Shared borrowing
+// ## Borrowing a shared reference
 //@ If you go back to our example with `vec_min`, and try to call that function twice, you will
 //@ get the same error. That's because `vec_min` demands that the caller transfers ownership of the
 //@ vector. Hence, when `vec_min` finishes, the entire vector is deleted. That's of course not what
 //@ we wanted! Can't we somehow give `vec_min` access to the vector, while retaining ownership of it?
 //@ 
-//@ Rust calls this *borrowing* the vector, and it works a bit like borrowing does in the real world:
-//@ If you borrow a book to your friend, your friend can have it and work on it (and you can't!)
-//@ as long as the book is still borrowed. Your friend could even borrow the book to someone else.
-//@ Eventually however, your friend has to give the book back to you, at which point you again
-//@ have full control.
+//@ Rust calls this *a reference* the vector, and it considers references as *borrowing* ownership. This
+//@ works a bit like borrowing does in the real world: If you borrow a book to your friend, your friend
+//@ can have it and work on it (and you can't!) as long as the book is still borrowed. Your friend could
+//@ even borrow the book to someone else. Eventually however, your friend has to give the book back to you,
+//@ at which point you again have full control.
 //@ 
-//@ Rust distinguishes between two kinds of borrows. First of all, there's the *shared* borrow.
-//@ This is where the book metaphor kind of breaks down... you can give a shared borrow of
+//@ Rust distinguishes between two kinds of references. First of all, there's the *shared* reference.
+//@ This is where the book metaphor kind of breaks down... you can give a shared reference to
 //@ *the same data* to lots of different people, who can all access the data. This of course
-//@ introduces aliasing, so in order to live up to its promise of safety, Rust does not allow
-//@ mutation through a shared borrow.
+//@ introduces aliasing, so in order to live up to its promise of safety, Rust generally does not allow
+//@ mutation through a shared reference.
 
-//@ So, let's re-write `vec_min` to work on a shared borrow of a vector, written `&Vec<i32>`.
+//@ So, let's re-write `vec_min` to work on a shared reference to a vector, written `&Vec<i32>`.
 //@ I also took the liberty to convert the function from `SomethingOrNothing` to the standard
 //@ library type `Option`.
 fn vec_min(v: &Vec<i32>) -> Option<i32> {
     use std::cmp;
 
     let mut min = None;
-    // This time, we explicitly request an iterator for the vector `v`. The method `iter` borrows the vector
-    // it works on, and provides shared borrows of the elements.
+    // This time, we explicitly request an iterator for the vector `v`. The method `iter` just borrows the vector
+    // it works on, and provides shared references to the elements.
     for e in v.iter() {
         // In the loop, `e` now has type `&i32`, so we have to dereference it to obtain an `i32`.
         min = Some(match min {
@@ -81,64 +81,64 @@ fn vec_min(v: &Vec<i32>) -> Option<i32> {
 }
 
 // Now that `vec_min` does not acquire ownership of the vector anymore, we can call it multiple times on the same vector and also do things like
-fn shared_borrow_demo() {
+fn shared_ref_demo() {
     let v = vec![5,4,3,2,1];
     let first = &v[0];
     vec_min(&v);
     vec_min(&v);
     println!("The first element is: {}", *first);
 }
-//@ What's going on here? First, `&` is how you create a shared borrow to something. All borrows are created like
-//@ this - there is no way to have something like a  NULL pointer. This code creates three shared borrows to `v`:
-//@ The borrow for `first` begins in the 2nd line of the function and lasts all the way to the end. The other two
-//@ borrows, created for calling `vec_min`, only last for the duration of that respective call.
+//@ What's going on here? First, `&` is how you borrow ownership to someone - this operator creates a shared reference.
+//@ `shared_ref_demo` creates three shared references to `v`:
+//@ The reference `first` begins in the 2nd line of the function and lasts all the way to the end. The other two
+//@ references, created for calling `vec_min`, only last for the duration of that respective call.
 //@ 
-//@ Technically, of course, borrows are pointers. Notice that since `vec_min` only gets a shared
-//@ borrow, Rust knows that it cannot mutate `v` in any way. Hence the pointer into the buffer of `v`
+//@ Technically, of course, references are pointers. Notice that since `vec_min` only gets a shared
+//@ reference, Rust knows that it cannot mutate `v`. Hence the pointer into the buffer of `v`
 //@ that was created before calling `vec_min` remains valid.
 
-// ## Mutable borrowing
-//@ There is a second kind of borrow, a *mutable borrow*. As the name suggests, such a borrow permits
-//@ mutation, and hence has to prevent aliasing. There can only ever be one mutable borrow to a
-//@ particular piece of data.
+// ## Exclusive, mutable references
+//@ There is a second way to borrow something, a second kind of reference: The *exclusive reference*. This is a reference that comes with the promise that
+//@ nobody else has *any kind of access* to the referee - there is no aliasing. As a consequence, it is always safe to mutate data through
+//@ an exclusive reference, which is why they are usually called *mutable references*.
 
 //@ As an example, consider a function which increments every element of a vector by 1.
-//@ The type `&mut Vec<i32>` is the type of mutable borrows of `vec<i32>`. Because the borrow is
-//@ mutable, we can use a mutable iterator, providing a mutable borrow of the elements.
+//@ The type `&mut Vec<i32>` is the type of mutable references to `vec<i32>`. Because the reference is
+//@ mutable, we can use a mutable iterator, providing mutable (exclusive) references to the elements.
 fn vec_inc(v: &mut Vec<i32>) {
     for e in v.iter_mut() {
         *e += 1;
     }
 }
 // Here's an example of calling `vec_inc`.
-fn mutable_borrow_demo() {
+fn mutable_ref_demo() {
     let mut v = vec![5,4,3,2,1];
     /* let first = &v[0]; */
     vec_inc(&mut v);
     vec_inc(&mut v);
     /* println!("The first element is: {}", *first); */             /* BAD! */
 }
-//@ `&mut` is the operator to create a mutable borrow. We have to mark `v` as mutable in order to create such a
-//@ borrow: Even though we completely own `v`, Rust tries to protect us from accidentally mutating things.
-//@ Hence owned variables that you intend to mutate, have to be annotated with `mut`.
-//@ Because the borrow passed to `vec_inc` only lasts as long as the function call, we can still call
-//@ `vec_inc` on the same vector twice: The durations of the two borrows do not overlap, so we never have more
-//@ than one mutable borrow. However, we can *not* create a shared borrow that spans a call to `vec_inc`. Just try
+//@ `&mut` is the operator to create a mutable reference. We have to mark `v` as mutable in order to create such a
+//@ reference: Even though we completely own `v`, Rust tries to protect us from accidentally mutating things.
+//@ Hence owned variables that you intend to mutate have to be annotated with `mut`.
+//@ Because the reference passed to `vec_inc` only lasts as long as the function call, we can still call
+//@ `vec_inc` on the same vector twice: The durations of the two references do not overlap, so we never have more
+//@ than one mutable reference - we only ever borrow `v` once at a time. However, we can *not* create a shared reference that spans a call to `vec_inc`. Just try
 //@ enabling the commented-out lines, and watch Rust complain. This is because `vec_inc` could mutate
-//@ the vector structurally (i.e., it could add or remove elements), and hence the pointer `first`
+//@ the vector structurally (i.e., it could add or remove elements), and hence the reference `first`
 //@ could become invalid. In other words, Rust keeps us safe from bugs like the one in the C++ snipped above.
 //@ 
-//@ Above, I said that having a mutable borrow excludes aliasing. But if you look at the code above carefully,
-//@ you may say: "Wait! Don't the `v` in `mutable_borrow_demo` and the `v` in `vec_inc` alias?" And you are right,
-//@ they do. However, the `v` in `mutable_borrow_demo` is not actually usable, it is not *active*: As long as there is an
-//@ outstanding borrow, Rust will not allow you to do anything with `v`.
+//@ Above, I said that having a mutable reference excludes aliasing. But if you look at the code above carefully,
+//@ you may say: "Wait! Don't the `v` in `mutable_ref_demo` and the `v` in `vec_inc` alias?" And you are right,
+//@ they do. However, the `v` in `mutable_ref_demo` is not actually usable, it is not *active*: As long as `v` is
+//@ borrowed, Rust will not allow you to do anything with it.
 
 // ## Summary
 // The ownership and borrowing system of Rust enforces the following three rules:
 // 
 // * There is always exactly one owner of a piece of data
-// * If there is an active mutable borrow, then nobody else can have active access to the data
-// * If there is an active shared borrow, then every other active access to the data is also a shared borrow
+// * If there is an active mutable reference, then nobody else can have active access to the data
+// * If there is an active shared reference, then every other active access to the data is also a shared reference
 // 
 // As it turns out, combined with the abstraction facilities of Rust, this is a very powerful mechanism
 // to tackle many problems beyond basic memory safety. You will see some examples for this soon.
index a0eb1d1530e5c54921af9e4bb627c9f418008eff..71d712ab9584c01b9b8b3922b1547e6ed414966d 100644 (file)
@@ -66,13 +66,13 @@ impl BigInt {
 //@ consumes the vector `v`. The caller hence loses access to its vector. However, there is something
 //@ we can do if we don't want that to happen: We can explicitly `clone` the vector,
 //@ which means that a full (or *deep*) copy will be performed. Technically,
-//@ `clone` takes a borrowed vector, and returns a fully owned one.
+//@ `clone` takes a borrowed vector in the form of a shared reference, and returns a fully owned one.
 fn clone_demo() {
     let v = vec![0,1 << 16];
     let b1 = BigInt::from_vec((&v).clone());
     let b2 = BigInt::from_vec(v);
 }
-//@ Rust has special treatment for methods that borrow its `self` argument (like `clone`, or
+//@ Rust has special treatment for methods that borrow their `self` argument (like `clone`, or
 //@ like `test_invariant` above): It is not necessary to explicitly borrow the receiver of the
 //@ method. Hence you could replace `(&v).clone()` by `v.clone()` above. Just try it!
 
@@ -113,18 +113,18 @@ impl<T: Clone> Clone for SomethingOrNothing<T> {
 //@ `#[derive(Clone)]` right before the definition of `SomethingOrNothing`.
 
 // **Exercise 05.2**: Write some more functions on `BigInt`. What about a function that returns the number of
-// digits? The number of non-zero digits? The smallest/largest digit? Of course, these should all just borrow `self`.
+// digits? The number of non-zero digits? The smallest/largest digit? Of course, these should all take `self` as a shared reference (i.e., in borrowed form).
 
 // ## Mutation + aliasing considered harmful (part 2)
-//@ Now that we know how to borrow a part of an `enum` (like `v` above), there's another example for why we
+//@ Now that we know how to create references to contents of an `enum` (like `v` above), there's another example we can look at for why we
 //@ have to rule out mutation in the presence of aliasing. First, we define an `enum` that can hold either
 //@ a number, or a string.
 enum Variant {
     Number(i32),
     Text(String),
 }
-//@ Now consider the following piece of code. Like above, `n` will be a borrow of a part of `var`,
-//@ and since we wrote `ref mut`, the borrow will be mutable. In other words, right after the match, `ptr`
+//@ Now consider the following piece of code. Like above, `n` will be a reference to a part of `var`,
+//@ and since we wrote `ref mut`, the reference will be exclusive and mutable. In other words, right after the match, `ptr`
 //@ points to the number that's stored in `var`, where `var` is a `Number`. Remember that `_` means
 //@ "we don't care".
 fn work_on_variant(mut var: Variant, text: String) {
index 5be00aa31eae4a4f3d9f51d30a42631309653784..8f9a723bc2d02a42e045e24f01c86900ccdda0a0 100644 (file)
@@ -28,7 +28,7 @@ impl BigInt {
 // Now we can write `vec_min`.
 fn vec_min(v: &Vec<BigInt>) -> Option<BigInt> {
     let mut min: Option<BigInt> = None;
-    // If `v` is a shared borrowed vector, then the default for iterating over it is to call `iter`, the iterator that borrows the elements.
+    // If `v` is a shared reference to a vector, then the default for iterating over it is to call `iter`, the iterator that borrows the elements.
     for e in v {
         let e = e.clone();
         min = Some(match min {                                      /*@*/
@@ -47,7 +47,7 @@ fn vec_min(v: &Vec<BigInt>) -> Option<BigInt> {
 //@ the intermediate variable `min`, which also has type `Option<BigInt>`. If you replace get rid of the
 //@ `e.clone()`, Rust will complain "Cannot move out of borrowed content". That's because
 //@ `e` is a `&BigInt`. Assigning `min = Some(*e)` works just like a function call: Ownership of the
-//@ underlying data is transferred from where `e` borrows from to `min`. But that's not allowed, since
+//@ underlying data is transferred from `e` to `min`. But that's not allowed, since
 //@ we just borrowed `e`, so we cannot empty it! We can, however, call `clone` on it. Then we own
 //@ the copy that was created, and hence we can store it in `min`. <br/>
 //@ Of course, making such a full copy is expensive, so we'd like to avoid it. We'll come to that in the next part.
@@ -94,10 +94,11 @@ impl<T: Copy> Copy for SomethingOrNothing<T> {}
 // ## Lifetimes
 //@ To fix the performance problems of `vec_min`, we need to avoid using `clone`. We'd like
 //@ the return value to not be owned (remember that this was the source of our need for cloning), but *borrowed*.
+//@ In other words, we want to return a shared reference to the minimal element.
 
-//@ The function `head` demonstrates how that could work: It borrows the first element of a vector if it is non-empty.
+//@ The function `head` demonstrates how that could work: It returns a reference to the first element of a vector if it is non-empty.
 //@ The type of the function says that it will either return nothing, or it will return a borrowed `T`.
-//@ We can then borrow the first element of `v` and use it to construct the return value.
+//@ We can then obtain a reference to the first element of `v` and use it to construct the return value.
 fn head<T>(v: &Vec<T>) -> Option<&T> {
     if v.len() > 0 {
         Some(&v[0])                                                 /*@*/
@@ -126,25 +127,25 @@ fn rust_foo(mut v: Vec<i32>) -> i32 {
     *first.unwrap()
 }
 
-//@ To give the answer to this question, we have to talk about the *lifetime* of a borrow. The point is, saying that
+//@ To give the answer to this question, we have to talk about the *lifetime* of a reference. The point is, saying that
 //@ you borrowed your friend a `Vec<i32>`, or a book, is not good enough, unless you also agree on *how long*
 //@ your friend can borrow it. After all, you need to know when you can rely on owning your data (or book) again.
 //@ 
-//@ Every borrow in Rust has an associated lifetime, written `&'a T` for a borrow of type `T` with lifetime `'a`. The full
+//@ Every reference in Rust has an associated lifetime, written `&'a T` for a reference with lifetime `'a` to something of type `T`. The full
 //@ type of `head` reads as follows: `fn<'a, T>(&'a Vec<T>) -> Option<&'a T>`. Here, `'a` is a *lifetime variable*, which
-//@ represents how long the vector has been borrowed. The function type expresses that argument and return value have *the same lifetime*.
+//@ represents for how long the vector has been borrowed. The function type expresses that argument and return value have *the same lifetime*.
 //@ 
 //@ When analyzing the code of `rust_foo`, Rust has to assign a lifetime to `first`. It will choose the scope
 //@ where `first` is valid, which is the entire rest of the function. Because `head` ties the lifetime of its
 //@ argument and return value together, this means that `&v` also has to borrow `v` for the entire duration of
-//@ the function `rust_foo`. So when we try to borrow `v` as mutable for `push`, Rust complains that the two borrows (the one
+//@ the function `rust_foo`. So when we try to borrow `v` exclusively for `push`, Rust complains that the two references (the one
 //@ for `head`, and the one for `push`) overlap. Lucky us! Rust caught our mistake and made sure we don't crash the program.
 //@ 
-//@ So, to sum this up: Lifetimes enable Rust to reason about *how long* a pointer has been borrowed. We can thus
-//@ safely write functions like `head`, that return pointers into data they got as argument, and make sure they
+//@ So, to sum this up: Lifetimes enable Rust to reason about *how long* a reference is valid, how long ownership has been borrowed. We can thus
+//@ safely write functions like `head`, that return references into data they got as argument, and make sure they
 //@ are used correctly, *while looking only at the function type*. At no point in our analysis of `rust_foo` did
 //@ we have to look *into* `head`. That's, of course, crucial if we want to separate library code from application code.
-//@ Most of the time, we don't have to explicitly add lifetimes to function types. This is thanks to *lifetimes elision*,
+//@ Most of the time, we don't have to explicitly add lifetimes to function types. This is thanks to *lifetime elision*,
 //@ where Rust will automatically insert lifetimes we did not specify, following some [simple, well-documented rules](https://doc.rust-lang.org/stable/book/lifetimes.html#lifetime-elision).
 
 //@ [index](main.html) | [previous](part05.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part06.rs) | [next](part07.html)
index 85218bc5fffe6db2fda2c4c6480974b23c9f43f8..2d88390c82f88956c91d2e3c3149288374bf7b29 100644 (file)
@@ -4,8 +4,8 @@
 pub use part05::BigInt;
 
 // With our new knowledge of lifetimes, we are now able to write down the desired type of `min`:
-//@ We want the function to take two borrows *of the same lifetime*, and then
-//@ return a borrow of that lifetime. If the two input lifetimes would be different, we
+//@ We want the function to take two references *with the same lifetime*, and then
+//@ return a reference with that lifetime. If the two input lifetimes would be different, we
 //@ would not know which lifetime to use for the result.
 pub trait Minimum {
     fn min<'a>(&'a self, other: &'a Self) -> &'a Self;
index 2c6dfc5a3679c215a466ee56ebf47fc6e212bb99..340de24a88e071f2e2ab68fd54ee646b85fd7e04 100644 (file)
@@ -91,13 +91,13 @@ impl ops::Add<BigInt> for BigInt {
     }
 }
 
-// ## Traits and borrowed types
+// ## Traits and reference types
 //@ If you inspect the addition function above closely, you will notice that it actually consumes ownership of both operands
 //@ to produce the result. This is, of course, in general not what we want. We'd rather like to be able to add two `&BigInt`.
 
 // Writing this out becomes a bit tedious, because trait implementations (unlike functions) require full explicit annotation
 // of lifetimes. Make sure you understand exactly what the following definition says. Notice that we can implement a trait for
-// a borrowed type!
+// a reference type!
 impl<'a, 'b> ops::Add<&'a BigInt> for &'b BigInt {
     type Output = BigInt;
     fn add(self, rhs: &'a BigInt) -> Self::Output {
index d4b3e66f2ebe0c0102814d5bd3fbe427133f14d6..ecb9ffaab1c801c9d7445937f9514d2bc411869f 100644 (file)
@@ -14,9 +14,9 @@ use part05::BigInt;
 //@ digit comes first. So, we have to write down some type, and implement `Iterator` for it such that `next` returns the digits
 //@ one-by-one. Clearly, the iterator must somehow be able to access the number it iterates over, and it must store its current
 //@ location. However, it cannot *own* the `BigInt`, because then the number would be gone after iteration! That'd certainly be bad.
-//@ The only alternative is for the iterator to *borrow* the number.
+//@ The only alternative is for the iterator to *borrow* the number, so it takes a reference.
 
-//@ In writing this down, we again have to be explicit about the lifetime of the borrow: We can't just have an
+//@ In writing this down, we again have to be explicit about the lifetime of the reference: We can't just have an
 //@ `Iter`, we must have an `Iter<'a>` that borrows the number for lifetime `'a`. This is our first example of
 //@ a data-type that's polymorphic in a lifetime, as opposed to a type. <br/>
 //@ `usize` here is the type of unsigned, pointer-sized numbers. It is typically the type of "lengths of things",
@@ -47,7 +47,7 @@ impl<'a> Iterator for Iter<'a> {
 // All we need now is a function that creates such an iterator for a given `BigInt`.
 impl BigInt {
     //@ Notice that when we write the type of `iter`, we don't actually have to give the lifetime parameter of `Iter`. Just as it is
-    //@ the case with functions returning borrowed data, you can elide the lifetime. The rules for adding the lifetimes are exactly the
+    //@ the case with functions returning references, you can elide the lifetime. The rules for adding the lifetimes are exactly the
     //@ same. (See the last section of [part 06](part06.html).)
     fn iter(&self) -> Iter {
         Iter { num: self, idx: self.data.len() }                    /*@*/
@@ -94,7 +94,7 @@ fn print_digits_v2(b: &BigInt) {
 
 // ## Iterator invalidation and lifetimes
 //@ You may have been surprised that we had to explicitly annotate a lifetime when we wrote `Iter`. Of
-//@ course, with lifetimes being present at every borrow in Rust, this is only consistent. But do we at
+//@ course, with lifetimes being present at every reference in Rust, this is only consistent. But do we at
 //@ least gain something from this extra annotation burden? (Thankfully, this burden only occurs when we
 //@ define *types*, and not when we define functions - which is typically much more common.)
 
@@ -130,7 +130,7 @@ fn iter_invalidation_demo() {
 //@ of the right type, the conversion function will not do anything and trivially be optimized away.
 
 //@ If you have a look at the documentation of `IntoIterator`, you will notice that the function `into_iter` it provides actually
-//@ consumes its argument. So we implement the trait for *borrowed* numbers, such that the number is not lost after the iteration.
+//@ consumes its argument. So we implement the trait for *references to* numbers, such that the number is not lost after the iteration.
 impl<'a> IntoIterator for &'a BigInt {
     type Item = u64;
     type IntoIter = Iter<'a>;
@@ -140,7 +140,7 @@ impl<'a> IntoIterator for &'a BigInt {
 }
 // With this in place, you can now replace `b.iter()` in `main` by `&b`. Go ahead and try it! <br/>
 //@ Wait, `&b`? Why that? Well, we implemented `IntoIterator` for `&BigInt`. If we are in a place where `b` is already borrowed, we can
-//@ just do `for digit in b`. If however, we own `b`, we have to borrow it. Alternatively, we could implement `IntoIterator`
+//@ just do `for digit in b`. If however, we own `b`, we have to create a reference to it. Alternatively, we could implement `IntoIterator`
 //@ for `BigInt` - which, as already mentioned, would mean that `b` is actually consumed by the iteration, and gone. This can easily happen,
 //@ for example, with a `Vec`: Both `Vec` and `&Vec` (and `&mut Vec`) implement `IntoIterator`, so if you do `for e in v`, and `v` has type `Vec`,
 //@ then you will obtain ownership of the elements during the iteration - and destroy the vector in the process. We actually did that in
index 696988913c9dd7d79851016b35f7819708562f7e..b586cd5057d3da50b4319dc207d324c589f2e4c0 100644 (file)
@@ -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<A: Action>(&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);                                     /*@*/
         }
@@ -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`.
 
@@ -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);
 }
index 9217be831b713c3837b2416fd0300499db53f16c..a5cafa1ca6794419e1d29184b862ab0b1b0b98d7 100644 (file)
@@ -48,7 +48,7 @@ impl Callbacks {
     // We can also write a generic version of `register`, such that it will be instantiated with some concrete closure type `F`
     // and do the creation of the `Box` and the conversion from `F` to `FnMut(i32)` itself.
     
-    //@ For this to work, we need to demand that the type `F` does not contain any short-lived borrows. After all, we will store it
+    //@ For this to work, we need to demand that the type `F` does not contain any short-lived references. After all, we will store it
     //@ in our list of callbacks indefinitely. If the closure contained a pointer to our caller's stackframe, that pointer
     //@ could be invalid by the time the closure is called. We can mitigate this by bounding `F` by a *lifetime*: `F: 'a` says
     //@ that all data of type `F` will *outlive* (i.e., will be valid for at least as long as) lifetime `'a`.
@@ -64,13 +64,13 @@ impl Callbacks {
         // Since they are of type `FnMut`, we need to mutably iterate.
         for callback in self.callbacks.iter_mut() {
             //@ Here, `callback` has type `&mut Box<FnMut(i32)>`. We can make use of the fact that `Box` is a *smart pointer*: In
-            //@ particular, we can use it as if it were a normal pointer, and use `*` to get to its contents. Then we mutably borrow
-            //@ these contents, because we call a `FnMut`.
+            //@ particular, we can use it as if it were a normal reference, and use `*` to get to its contents. Then we obtain a
+            //@ mutable reference to these contents, because we call a `FnMut`.
             (&mut *callback)(val);                                  /*@*/
-            //@ Just like it is the case with normal borrows, this typically happens implicitly, so we can also directly call the function.
+            //@ Just like it is the case with normal references, this typically happens implicitly with smart pointers, so we can also directly call the function.
             //@ Try removing the `&mut *`.
             //@ 
-            //@ The difference to a normal pointer is that `Box` implies ownership: Once you drop the box (i.e., when the entire `Callbacks` instance is
+            //@ The difference to a reference is that `Box` implies full ownership: Once you drop the box (i.e., when the entire `Callbacks` instance is
             //@ dropped), the content it points to on the heap will be deleted.
         }
     }
@@ -83,9 +83,9 @@ pub fn main() {
     c.call(0);
 
     {
-        //@ We can even register callbacks that modify their environment. Per default, Rust will attempt to borrow `count`. However,
+        //@ We can even register callbacks that modify their environment. Per default, Rust will attempt to capture a reference to `count`, to borrow it. However,
         //@ that doesn't work out this time. Remember the `'static` bound above? Borrowing `count` in the environment would
-        //@ violate that bound, as the borrow is only valid for this block. If the callbacks are triggered later, we'd be in trouble.
+        //@ violate that bound, as the reference is only valid for this block. If the callbacks are triggered later, we'd be in trouble.
         //@ We have to explicitly tell Rust to `move` ownership of the variable into the closure. Its environment will then contain a
         //@ `usize` rather than a `&mut usize`, and the closure has no effect on this local variable anymore.
         let mut count: usize = 0;
@@ -99,7 +99,7 @@ pub fn main() {
 
 //@ ## Run-time behavior
 //@ When you run the program above, how does Rust know what to do with the callbacks? Since an unsized type lacks some information,
-//@ a *pointer* to such a type (be it a `Box` or a borrow) will need to complete this information. We say that pointers to
+//@ a *pointer* to such a type (be it a `Box` or a reference) will need to complete this information. We say that pointers to
 //@ trait objects are *fat*. They store not only the address of the object, but (in the case of trait objects) also a *vtable*: A
 //@ table of function pointers, determining the code that's run when a trait method is called. There are some restrictions for traits to be usable
 //@ as trait objects. This is called *object safety* and described in [the documentation](https://doc.rust-lang.org/stable/book/trait-objects.html) and [the reference](https://doc.rust-lang.org/reference.html#trait-objects).
@@ -113,8 +113,8 @@ pub fn main() {
 //@ (Of course, in the case of `register` above, there's no function called on the trait object.)
 //@ Isn't it beautiful how traits can nicely handle this tradeoff (and much more, as we saw, like closures and operator overloading)?
 
-// **Exercise 11.1**: We made the arbitrary choice of using `i32` for the arguments. Generalize the data-structures above
+// **Exercise 11.1**: We made the arbitrary choice of using `i32` for the arguments. Generalize the data structures above
 // to work with an arbitrary type `T` that's passed to the callbacks. Since you need to call multiple callbacks with the
-// same `t: T`, you will either have to restrict `T` to `Copy` types, or pass a borrow.
+// same `t: T`, you will either have to restrict `T` to `Copy` types, or pass a reference.
 
 //@ [index](main.html) | [previous](part10.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part11.rs) | [next](part12.html)
index d88a8a6fbc038cd3e6ff11d915f68d07c8d1c7ce..8b04fb669ef23083556b7672b76306bdaf0ab881 100644 (file)
@@ -16,10 +16,10 @@ use std::cell::{Cell, RefCell};
 //@ references are gone, the data is deleted.
 //@ 
 //@ Wait a moment, you may say here. Multiple references to the same data? That's aliasing! Indeed:
-//@ Once data is stored in an `Rc`, it is read-only and you can only ever get a shared borrow of the data again.
+//@ Once data is stored in an `Rc`, it is read-only and you can only ever get a shared reference to the data again.
 
-//@ Because of this read-only restriction, we cannot use `FnMut` here: We'd be unable to call the function with a mutable borrow
-//@ of it's environment! So we have to go with `Fn`. We wrap that in an `Rc`, and then Rust happily derives `Clone` for us.
+//@ Because of this read-only restriction, we cannot use `FnMut` here: We'd be unable to call the function with a mutable reference
+//@ to it's environment! So we have to go with `Fn`. We wrap that in an `Rc`, and then Rust happily derives `Clone` for us.
 #[derive(Clone)]
 struct Callbacks {
     callbacks: Vec<Rc<Fn(i32)>>,
@@ -61,8 +61,8 @@ pub fn main() {
 //@ So it would be rather sad if we were not able to write this program. Lucky enough, Rust's standard library provides a
 //@ solution in the form of `Cell<T>`. This type represents a memory cell of some type `T`, providing the two basic operations
 //@ `get` and `set`. `get` returns a *copy* of the content of the cell, so all this works only if `T` is `Copy`.
-//@ `set`, which overrides the content, only needs a *shared borrow* of the cell. The phenomenon of a type that permits mutation through
-//@ shared borrows (i.e., mutation despite the possibility of aliasing) is called *interior mutability*. You can think
+//@ `set`, which overrides the content, only needs a *shared reference* to the cell. The phenomenon of a type that permits mutation through
+//@ shared references (i.e., mutation despite the possibility of aliasing) is called *interior mutability*. You can think
 //@ of `set` changing only the *contents* of the cell, not its *identity*. In contrast, the kind of mutation we saw so far was
 //@ about replacing one piece of data by something else of the same type. This is called *inherited mutability*. <br/>
 //@ Notice that it is impossible to *borrow* the contents of the cell, and that is actually the key to why this is safe.
@@ -73,7 +73,7 @@ fn demo_cell(c: &mut Callbacks) {
         let count = Cell::new(0);
         // Again, we have to move ownership if the `count` into the environment closure.
         c.register(move |val| {
-            // In here, all we have is a shared borrow of our environment. But that's good enough for the `get` and `set` of the cell!
+            // In here, all we have is a shared reference of our environment. But that's good enough for the `get` and `set` of the cell!
             //@ At run-time, the `Cell` will be almost entirely compiled away, so this becomes pretty much equivalent to the version
             //@ we wrote in the previous part.
             let new_count = count.get()+1;
@@ -86,8 +86,13 @@ fn demo_cell(c: &mut Callbacks) {
 }
 
 //@ It is worth mentioning that `Rc` itself also has to make use of interior mutability: When you `clone` an `Rc`, all it has available
-//@ is a shared borrow. However, it has to increment the reference count! Internally, `Rc` uses `Cell` for the count, such that it
+//@ is a shared reference. However, it has to increment the reference count! Internally, `Rc` uses `Cell` for the count, such that it
 //@ can be updated during `clone`.
+//@ 
+//@ Putting it all together, the story around mutation and ownership through references looks as follows: There are *exclusive* references,
+//@ which - because of their exclusivity - are always safe to mutate through. And there are *shared* references, where the compiler cannot
+//@ generally promise that mutation is safe. However, if extra circumstances guarantee that mutation *is* safe, then it can happen even
+//@ through a sahred reference - as we saw with `Cell`.
 
 // ## `RefCell`
 //@ As the next step in the evolution of `Callbacks`, we could try to solve this problem of mutability once and for all, by adding `Cell`
@@ -120,23 +125,23 @@ impl CallbacksMut {
     pub fn call(&mut self, val: i32) {
         for callback in self.callbacks.iter() {
             // We have to *explicitly* borrow the contents of a `RefCell` by calling `borrow` or `borrow_mut`.
-            //@ At run-time, the cell will keep track of the number of outstanding shared and mutable borrows,
+            //@ At run-time, the cell will keep track of the number of outstanding shared and mutable references,
             //@ and panic if the rules are violated. <br />
-            //@ For this check to be performed, `closure` is a *guard*: Rather than a normal borrow, `borrow_mut` returns
-            //@ a smart pointer (`RefMut`, in this case) that waits until is goes out of scope, and then
-            //@ appropriately updates the number of active borrows.
+            //@ For this check to be performed, `closure` is a *guard*: Rather than a normal reference, `borrow_mut` returns
+            //@ a smart pointer ([`RefMut`](https://doc.rust-lang.org/stable/std/cell/struct.RefMut.html), in this case) that waits until is goes out of scope, and then
+            //@ appropriately updates the number of active references.
             //@ 
             //@ Since `call` is the only place that borrows the environments of the closures, we should expect that
-            //@ the check will always succeed. However, this is not actually true. Several different `CallbacksMut` could share
+            //@ the check will always succeed, as is actually entirely useless. However, this is not actually true. Several different `CallbacksMut` could share
             //@ a callback (as they were created with `clone`), and calling one callback here could trigger calling
-            //@ all callbacks of the other `CallbacksMut`, which would end up calling the initial callback again. This issue is called *reentrancy*,
-            //@ and it can lead to subtle bugs. Here, it would mean that the closure runs twice, each time thinking it has the only
-            //@ mutable borrow of its environment - so it may end up dereferencing a dangling pointer. Ouch! Lucky enough,
+            //@ all callbacks of the other `CallbacksMut`, which would end up calling the initial callback again. This issue of functions accidentally recursively calling
+            //@ themselves is called *reentrancy*, and it can lead to subtle bugs. Here, it would mean that the closure runs twice, each time thinking it has an
+            //@ exclusive, mutable reference to its environment - so it may end up dereferencing a dangling pointer. Ouch! Lucky enough,
             //@ Rust detects this at run-time and panics once we try to borrow the same environment again. I hope this also makes it
             //@ clear that there's absolutely no hope of Rust performing these checks statically, at compile-time: It would have to detect reentrancy!
             let mut closure = callback.borrow_mut();
             // Unfortunately, Rust's auto-dereference of pointers is not clever enough here. We thus have to explicitly
-            // dereference the smart pointer and obtain a mutable borrow of the content.
+            // dereference the smart pointer and obtain a mutable reference to the content.
             (&mut *closure)(val);
         }
     }
@@ -159,6 +164,6 @@ fn demo_mut(c: &mut CallbacksMut) {
 }
 
 // **Exercise 12.1**: Write some piece of code using only the available, public interface of `CallbacksMut` such that a reentrant call to a closure
-// is happening, and the program aborts because the `RefCell` refuses to hand out a second mutable borrow of the closure's environment.
+// is happening, and the program panics because the `RefCell` refuses to hand out a second mutable borrow of the closure's environment.
 
 //@ [index](main.html) | [previous](part11.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part12.rs) | [next](part13.html)
index 7aedb71493dbc1f229d79ad8cd468bea1eb96bec..4fbc908cab5043b8eb486ce6f7d97eecded1d251 100644 (file)
@@ -159,8 +159,8 @@ pub fn main() {
 //@ programs memory safe, and that prevents us from invalidating iterators, also helps secure our multi-threaded code against
 //@ data races. For example, notice how `read_files` sends a `String` to `filter_lines`. At run-time, only the pointer to
 //@ the character data will actually be moved around (just like when a `String` is passed to a function with full ownership). However,
-//@ `read_files` has to *give up* ownership of the string to perform `send`, to it is impossible for an outstanding borrow to
-//@ still be around. After it sent the string to the other side, `read_files` has no pointer into the string content
+//@ `read_files` has to *give up* ownership of the string to perform `send`, to it is impossible for the string to still be borrowed.
+//@ After it sent the string to the other side, `read_files` has no pointer into the string content
 //@ anymore, and hence no way to race on the data with someone else.
 //@ 
 //@ There is a little more to this. Remember the `'static` bound we had to add to `register` in the previous parts, to make
index 45044afe48fc9dd513816e9b37e7e94f470fd547..ae13e01af7d7b12c49e52469a28342da103c00b6 100644 (file)
@@ -15,8 +15,8 @@
 //@ `[T]` is the type of an (unsized) *array*, with elements of type `T`. All this means is that there's a contiguous
 //@ region of memory, where a bunch of `T` are stored. How many? We can't tell! This is an unsized type. Just like for
 //@ trait objects, this means we can only operate on pointers to that type, and these pointers will carry the missing
-//@ information - namely, the length. Such a pointer is called a *slice*. As we will see, a slice can be split.
-//@ Our function can thus take a borrowed slice, and promise to sort all elements in there.
+//@ information - namely, the length (they will be *fat pointers*). Such a reference to an array is called a *slice*. As we will see, a slice can be split.
+//@ Our function can thus take a mutable slice, and promise to sort all elements in there.
 pub fn sort<T: PartialOrd>(data: &mut [T]) {
     if data.len() < 2 { return; }
 
@@ -38,7 +38,7 @@ pub fn sort<T: PartialOrd>(data: &mut [T]) {
     // Finally, we split our slice to sort the two halves. The nice part about slices is that splitting them is cheap:
     //@ They are just a pointer to a start address, and a length. We can thus get two pointers, one at the beginning and
     //@ one in the middle, and set the lengths appropriately such that they don't overlap. This is what `split_at_mut` does.
-    //@ Since the two slices don't overlap, there is no aliasing and we can have them both mutably borrowed.
+    //@ Since the two slices don't overlap, there is no aliasing and we can have both of them as exclusive, mutable slices.
     let (part1, part2) = data.split_at_mut(lpos);
     //@ The index operation can not only be used to address certain elements, it can also be used for *slicing*: Giving a range
     //@ of indices, and obtaining an appropriate part of the slice we started with. Here, we remove the last element from
@@ -131,7 +131,7 @@ Options:
         //@ encoded string, that is, a bunch of bytes in memory (`[u8]`) that are valid according of UTF-8. `str` is unsized. `&str`
         //@ stores the address of the character data, and their length. String literals like "this one" are
         //@ of type `&'static str`: They point right to the constant section of the binary, so 
-        //@ the borrow is valid for the entire program. The bytes pointed to by `pattern`, on the other hand, are owned by someone else, 
+        //@ the reference is valid for the entire program. The bytes pointed to by `pattern`, on the other hand, are owned by someone else, 
         //@ and we call `to_string` on it to copy the string data into a buffer on the heap that we own.
         let mode = if count {
             OutputMode::Count
index 3b598255c0705f58d01c808700e31a5d670b3c01..47952d6995bb08a06b02602d61296a5b957209d3 100644 (file)
@@ -45,7 +45,7 @@ impl ConcurrentCounter {
         *counter = *counter + by;
         //@ At the end of the function, `counter` is dropped and the mutex is available again.
         //@ This can only happen when full ownership of the guard is given up. In particular, it is impossible for us
-        //@ to borrow some of its content, release the lock of the mutex, and subsequently access the protected data without holding
+        //@ to take a reference to some of its content, release the lock of the mutex, and subsequently access the protected data without holding
         //@ the lock. Enforcing the locking discipline is expressible in the Rust type system, so we don't have to worry
         //@ about data races *even though* we are mutating shared memory!
         //@ 
@@ -105,7 +105,7 @@ pub fn main() {
 //@ ## `RwLock`
 //@ Besides `Mutex`, there's also [`RwLock`](https://doc.rust-lang.org/stable/std/sync/struct.RwLock.html), which
 //@ provides two ways of locking: One that grants only read-only access, to any number of concurrent readers, and another one
-//@ for exclusive write access. Notice that this is the same pattern we already saw with shared vs. mutable borrows. Hence
+//@ for exclusive write access. Notice that this is the same pattern we already saw with shared vs. mutable references. Hence
 //@ another way of explaining `RwLock` is to say that it is like `RefCell`, but works even for concurrent access. Rather than
 //@ panicking when the data is already borrowed, `RwLock` will of course block the current thread until the lock is available.
 //@ In this view, `Mutex` is a stripped-down version of `RwLock` that does not distinguish readers and writers.
@@ -118,26 +118,26 @@ pub fn main() {
 //@ `RefCell` across multiple threads?
 //@ 
 //@ In part 13, we talked about types that are marked `Send` and thus can be moved to another thread. However, we did *not*
-//@ talk about the question whether a borrow is `Send`. For `&mut T`, the answer is: It is `Send` whenever `T` is send.
+//@ talk about the question whether a reference is `Send`. For `&mut T`, the answer is: It is `Send` whenever `T` is send.
 //@ `&mut` allows moving values back and forth, it is even possible to [`swap`](https://doc.rust-lang.org/stable/std/mem/fn.swap.html)
-//@ the contents of two mutably borrowed values. So in terms of concurrency, sending a mutable borrow is very much like
+//@ the contents of two mutable references. So in terms of concurrency, sending a mutable, exclusive reference is very much like
 //@ sending full ownership, in the sense that it can be used to move the object to another thread.
 //@ 
-//@ But what about `&T`, a shared borrow? Without interior mutability, it would always be all-right to send such values.
+//@ But what about `&T`, a shared reference? Without interior mutability, it would always be all-right to send such values.
 //@ After all, no mutation can be performed, so there can be as many threads accessing the data as we like. In the
 //@ presence of interior mutability though, the story gets more complicated. Rust introduces another marker trait for
 //@ this purpose: `Sync`. A type `T` is `Sync` if and only if `&T` is `Send`. Just like `Send`, `Sync` has a default implementation
 //@ and is thus automatically implemented for a data-structure *if* all its members implement it.
 //@ 
-//@ Since `Arc` provides multiple threads with a shared borrow of its content, `Arc<T>` is only `Send` if `T` is `Sync`.
+//@ Since `Arc` provides multiple threads with a shared reference to its content, `Arc<T>` is only `Send` if `T` is `Sync`.
 //@ So if we had used `RefCell` above, which is *not* `Sync`, Rust would have caught that mistake. Notice however that
 //@ `RefCell` *is* `Send`: If ownership of the entire cell is moved to another thread, it is still not possible for several
 //@ threads to try to access the data at the same time.
 //@ 
-//@ Almost all the types we saw so far are `Sync`, with the exception of `Rc`. Remember that a shared borrow is good enough
-//@ for cloning, and we don't want other threads to clone our local `Rc`, so it must not be `Sync`. The rule of `Mutex`
-//@ is to enforce synchronization, so it should not be entirely surprising that `Mutex<T>` is `Send` *and* `Sync` provided that
-//@ `T` is `Send`.
+//@ Almost all the types we saw so far are `Sync`, with the exception of `Rc`. Remember that a shared reference is good enough
+//@ for cloning, and we don't want other threads to clone our local `Rc` (they would race for updating the reference count),
+//@ so it must not be `Sync`. The rule of `Mutex` is to enforce synchronization, so it should not be entirely surprising that
+//@ `Mutex<T>` is `Send` *and* `Sync` provided that `T` is `Send`.
 //@ 
 //@ You may be curious whether there is a type that's `Sync`, but not `Send`. There are indeed rather esoteric examples
 //@ of such types, but that's not a topic I want to go into. In case you are curious, there's a
index 90b687bb7438c668c9a11cc2d0de98bdc3f8173d..748e7d9651e445106a8b4d9690888e8878575352 100644 (file)
@@ -32,7 +32,7 @@ struct Node<T> {
     data: T,
 }
 // A node pointer is a *mutable raw pointer* to a node.
-//@ Raw pointers (`*mut T` and `*const T`) are the Rust equivalent of pointers in C. Unlike borrows, they do not come with
+//@ Raw pointers (`*mut T` and `*const T`) are the Rust equivalent of pointers in C. Unlike references, they do not come with
 //@ any guarantees: Raw pointers can be null, or they can point to garbage. They don't have a lifetime, either.
 type NodePtr<T> = *mut Node<T>;
 
@@ -115,7 +115,7 @@ impl<T> LinkedList<T> {
 //@ that it is going to visit. However, how do we make sure that this pointer remains valid? We have to
 //@ get this right ourselves, as we left the safe realms of borrowing and ownership. Remember that the
 //@ key ingredient for iterator safety was to tie the lifetime of the iterator to the lifetime of the
-//@ borrow used for `iter_mut`. We will thus give `IterMut` two parameters: A type parameter specifying
+//@ reference used for `iter_mut`. We will thus give `IterMut` two parameters: A type parameter specifying
 //@ the type of the data, and a lifetime parameter specifying for how long the list was borrowed to the
 //@ iterator.
 
@@ -123,7 +123,7 @@ impl<T> LinkedList<T> {
 //@ the data in the list lives at least as long as the iterator: If you drop the `T: 'a`, Rust will tell
 //@ you to add it back. And secondly, Rust will complain if `'a` is not actually used in the struct.
 //@ It doesn't know what it is supposed to do with that lifetime. So we use `PhantomData` again to tell
-//@ it that in terms of ownership, this type actually (mutably) borrows a linked list. This has no
+//@ it that in terms of ownership, this type actually (exclusively) borrows a linked list. This has no
 //@ operational effect, but it means that Rust can deduce the intent we had when adding that
 //@ seemingly useless lifetime parameter.
 pub struct IterMut<'a, T> where T: 'a {
@@ -141,7 +141,7 @@ impl<'a, T> Iterator for IterMut<'a, T> {
         if self.next.is_null() {
             None
         } else {
-            // Otherwise, we can convert the next pointer to a borrow, get a borrow to the data
+            // Otherwise, we can convert the next pointer to a reference, get a reference to the data
             // and update the iterator.
             let next = unsafe { &mut *self.next };
             let ret = &mut next.data;
@@ -154,12 +154,12 @@ impl<'a, T> Iterator for IterMut<'a, T> {
 //@ In `next` above, we made crucial use of the assumption that `self.next` is either null or a valid pointer.
 //@ This only works because if someone tries to delete elements from a list during iteration, we know that the borrow checker
 //@ will catch them: If they call `next`, the lifetime `'a` we artificially added to the iterator has to still be
-//@ active, which means the mutable borrow passed to `iter_mut` is still active, which means nobody can delete
+//@ active, which means the mutable reference passed to `iter_mut` is still active, which means nobody can delete
 //@ anything from the list. In other words, we make use of the expressive type system of Rust, decorating
 //@ our own unsafe implementation with just enough information so that Rust can check *uses* of the linked-list.
 //@ If the type system were weaker, we could not write a linked-list like the above with a safe interface!
 
-// **Exercise 16.2**: Add a method `iter` and a type `Iter` providing iteration for shared borrows.
+// **Exercise 16.2**: Add a method `iter` and a type `Iter` providing iteration for shared references.
 // Add testcases for both kinds of iterators.
 
 // ## `Drop`
@@ -173,7 +173,7 @@ impl<'a, T> Iterator for IterMut<'a, T> {
 //@ of `LinkedList`.
 impl<T> Drop for LinkedList<T> {
     // The destructor itself is a method which takes `self` in mutably borrowed form. It cannot own `self`, because then
-    // the destructor of `self` would be called at the end of the function, resulting in endless recursion...
+    // the destructor of `self` would be called at the end of the function, resulting in endless recursion.
     fn drop(&mut self) {
         let mut cur_ptr = self.first;
         while !cur_ptr.is_null() {
index bde913f20cf6b7750591c0bc3849daaaa09ea250..2a3a6488a7536e88c43395dfeb1a257224a83404 100644 (file)
@@ -1,5 +1,5 @@
-// Rust-101, Part 04: Ownership, Borrowing
-// =======================================
+// Rust-101, Part 04: Ownership, Borrowing, References
+// ===================================================
 
 /*
   void foo(std::vector<int> v) {
@@ -17,14 +17,14 @@ fn ownership_demo() {
     /* println!("The first element is: {}", v[0]); */               /* BAD! */
 }
 
-// ## Shared borrowing
+// ## Borrowing a shared reference
 
 fn vec_min(v: &Vec<i32>) -> Option<i32> {
     use std::cmp;
 
     let mut min = None;
-    // This time, we explicitly request an iterator for the vector `v`. The method `iter` borrows the vector
-    // it works on, and provides shared borrows of the elements.
+    // This time, we explicitly request an iterator for the vector `v`. The method `iter` just borrows the vector
+    // it works on, and provides shared references to the elements.
     for e in v.iter() {
         // In the loop, `e` now has type `&i32`, so we have to dereference it to obtain an `i32`.
         min = Some(match min {
@@ -36,7 +36,7 @@ fn vec_min(v: &Vec<i32>) -> Option<i32> {
 }
 
 // Now that `vec_min` does not acquire ownership of the vector anymore, we can call it multiple times on the same vector and also do things like
-fn shared_borrow_demo() {
+fn shared_ref_demo() {
     let v = vec![5,4,3,2,1];
     let first = &v[0];
     vec_min(&v);
@@ -44,7 +44,7 @@ fn shared_borrow_demo() {
     println!("The first element is: {}", *first);
 }
 
-// ## Mutable borrowing
+// ## Exclusive, mutable references
 
 fn vec_inc(v: &mut Vec<i32>) {
     for e in v.iter_mut() {
@@ -52,7 +52,7 @@ fn vec_inc(v: &mut Vec<i32>) {
     }
 }
 // Here's an example of calling `vec_inc`.
-fn mutable_borrow_demo() {
+fn mutable_ref_demo() {
     let mut v = vec![5,4,3,2,1];
     /* let first = &v[0]; */
     vec_inc(&mut v);
@@ -64,8 +64,8 @@ fn mutable_borrow_demo() {
 // The ownership and borrowing system of Rust enforces the following three rules:
 // 
 // * There is always exactly one owner of a piece of data
-// * If there is an active mutable borrow, then nobody else can have active access to the data
-// * If there is an active shared borrow, then every other active access to the data is also a shared borrow
+// * If there is an active mutable reference, then nobody else can have active access to the data
+// * If there is an active shared reference, then every other active access to the data is also a shared reference
 // 
 // As it turns out, combined with the abstraction facilities of Rust, this is a very powerful mechanism
 // to tackle many problems beyond basic memory safety. You will see some examples for this soon.
index 65f6f7de10fd3142c9a240ebe5d1ef0799d0e18e..28c4030aef6f4200c77b983551c05a0ac0ef52ec 100644 (file)
@@ -61,7 +61,7 @@ impl<T: Clone> Clone for SomethingOrNothing<T> {
 }
 
 // **Exercise 05.2**: Write some more functions on `BigInt`. What about a function that returns the number of
-// digits? The number of non-zero digits? The smallest/largest digit? Of course, these should all just borrow `self`.
+// digits? The number of non-zero digits? The smallest/largest digit? Of course, these should all take `self` as a shared reference (i.e., in borrowed form).
 
 // ## Mutation + aliasing considered harmful (part 2)
 enum Variant {
index 04a08478f87d428dc75fadc6c1111b9fabce8356..3bc7d0834759df4346b3261feb93e330b6ffaf93 100644 (file)
@@ -25,7 +25,7 @@ impl BigInt {
 // Now we can write `vec_min`.
 fn vec_min(v: &Vec<BigInt>) -> Option<BigInt> {
     let mut min: Option<BigInt> = None;
-    // If `v` is a shared borrowed vector, then the default for iterating over it is to call `iter`, the iterator that borrows the elements.
+    // If `v` is a shared reference to a vector, then the default for iterating over it is to call `iter`, the iterator that borrows the elements.
     for e in v {
         let e = e.clone();
         unimplemented!()
index 01189060eb1b958256583d0eb1717bcc94319976..5ddcb3344cf8652ada5e6a3f0f5f4fe4e4cc5892 100644 (file)
@@ -56,11 +56,11 @@ impl ops::Add<BigInt> for BigInt {
     }
 }
 
-// ## Traits and borrowed types
+// ## Traits and reference types
 
 // Writing this out becomes a bit tedious, because trait implementations (unlike functions) require full explicit annotation
 // of lifetimes. Make sure you understand exactly what the following definition says. Notice that we can implement a trait for
-// a borrowed type!
+// a reference type!
 impl<'a, 'b> ops::Add<&'a BigInt> for &'b BigInt {
     type Output = BigInt;
     fn add(self, rhs: &'a BigInt) -> Self::Output {
index 746aed9e96398b6b03cc8f033cdeec25764919a2..a93ad07ed684bb301ac3d14b501f68cfe7713b97 100644 (file)
@@ -59,7 +59,7 @@ pub fn main() {
 }
 
 
-// **Exercise 11.1**: We made the arbitrary choice of using `i32` for the arguments. Generalize the data-structures above
+// **Exercise 11.1**: We made the arbitrary choice of using `i32` for the arguments. Generalize the data structures above
 // to work with an arbitrary type `T` that's passed to the callbacks. Since you need to call multiple callbacks with the
-// same `t: T`, you will either have to restrict `T` to `Copy` types, or pass a borrow.
+// same `t: T`, you will either have to restrict `T` to `Copy` types, or pass a reference.
 
index 86fb4bb14b350f192acb7cb9c2a9ff997c109e59..390b5dfa9530880a48a3feac7997154dcf774184 100644 (file)
@@ -48,7 +48,7 @@ fn demo_cell(c: &mut Callbacks) {
         let count = Cell::new(0);
         // Again, we have to move ownership if the `count` into the environment closure.
         c.register(move |val| {
-            // In here, all we have is a shared borrow of our environment. But that's good enough for the `get` and `set` of the cell!
+            // In here, all we have is a shared reference of our environment. But that's good enough for the `get` and `set` of the cell!
             let new_count = count.get()+1;
             count.set(new_count);
             println!("Callback 2: {} ({}. time)", val, new_count);
@@ -81,7 +81,7 @@ impl CallbacksMut {
             // We have to *explicitly* borrow the contents of a `RefCell` by calling `borrow` or `borrow_mut`.
             let mut closure = callback.borrow_mut();
             // Unfortunately, Rust's auto-dereference of pointers is not clever enough here. We thus have to explicitly
-            // dereference the smart pointer and obtain a mutable borrow of the content.
+            // dereference the smart pointer and obtain a mutable reference to the content.
             (&mut *closure)(val);
         }
     }
@@ -104,5 +104,5 @@ fn demo_mut(c: &mut CallbacksMut) {
 }
 
 // **Exercise 12.1**: Write some piece of code using only the available, public interface of `CallbacksMut` such that a reentrant call to a closure
-// is happening, and the program aborts because the `RefCell` refuses to hand out a second mutable borrow of the closure's environment.
+// is happening, and the program panics because the `RefCell` refuses to hand out a second mutable borrow of the closure's environment.
 
index 21cd60fe1d79db910e1483c4ad96afdc0811e2bd..fa5b1845f595632b7bf5ce506e5d365881c1db46 100644 (file)
@@ -80,7 +80,7 @@ impl<'a, T> Iterator for IterMut<'a, T> {
         if self.next.is_null() {
             None
         } else {
-            // Otherwise, we can convert the next pointer to a borrow, get a borrow to the data
+            // Otherwise, we can convert the next pointer to a reference, get a reference to the data
             // and update the iterator.
             let next = unsafe { &mut *self.next };
             let ret = &mut next.data;
@@ -90,14 +90,14 @@ impl<'a, T> Iterator for IterMut<'a, T> {
 }
 
 
-// **Exercise 16.2**: Add a method `iter` and a type `Iter` providing iteration for shared borrows.
+// **Exercise 16.2**: Add a method `iter` and a type `Iter` providing iteration for shared references.
 // Add testcases for both kinds of iterators.
 
 // ## `Drop`
 
 impl<T> Drop for LinkedList<T> {
     // The destructor itself is a method which takes `self` in mutably borrowed form. It cannot own `self`, because then
-    // the destructor of `self` would be called at the end of the function, resulting in endless recursion...
+    // the destructor of `self` would be called at the end of the function, resulting in endless recursion.
     fn drop(&mut self) {
         let mut cur_ptr = self.first;
         while !cur_ptr.is_null() {