From: Ralf Jung Date: Sun, 17 Jan 2016 14:20:09 +0000 (+0100) Subject: exclusive reference -> unique reference X-Git-Url: https://git.ralfj.de/rust-101.git/commitdiff_plain/3a94cd0261c4cd5c348eb1c1359106b76f01e518?ds=inline;hp=63b7a79069a399c32d575bb71832fcad134117c9 exclusive reference -> unique reference --- diff --git a/src/part04.rs b/src/part04.rs index fcfc8d9..754ba62 100644 --- a/src/part04.rs +++ b/src/part04.rs @@ -97,14 +97,14 @@ fn shared_ref_demo() { //@ 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. -// ## 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 +// ## Unique, mutable references +//@ There is a second way to borrow something, a second kind of reference: The *unique 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*. +//@ an unique 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` is the type of mutable references to `vec`. Because the reference is -//@ mutable, we can use a mutable iterator, providing mutable (exclusive) references to the elements. +//@ mutable, we can use a mutable iterator, providing mutable (unique) references to the elements. fn vec_inc(v: &mut Vec) { for e in v.iter_mut() { *e += 1; diff --git a/src/part05.rs b/src/part05.rs index 71d712a..784440c 100644 --- a/src/part05.rs +++ b/src/part05.rs @@ -124,7 +124,7 @@ enum Variant { Text(String), } //@ 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` +//@ and since we wrote `ref mut`, the reference will be uniqie 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) { diff --git a/src/part06.rs b/src/part06.rs index 8f9a723..9552eb5 100644 --- a/src/part06.rs +++ b/src/part06.rs @@ -138,8 +138,8 @@ fn rust_foo(mut v: Vec) -> i32 { //@ 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` 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. +//@ the function `rust_foo`. So when we try to create a unique reference to `v` for `push`, Rust complains that the two references (the one +//@ for `head`, and the one for `push`) overlap, so neither of them can be unique. 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 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 diff --git a/src/part12.rs b/src/part12.rs index 8b04fb6..34e994e 100644 --- a/src/part12.rs +++ b/src/part12.rs @@ -89,7 +89,7 @@ fn demo_cell(c: &mut Callbacks) { //@ 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, +//@ Putting it all together, the story around mutation and ownership through references looks as follows: There are *unique* 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`. @@ -135,8 +135,8 @@ impl CallbacksMut { //@ 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 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, + //@ 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 a + //@ unique, 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(); diff --git a/src/part14.rs b/src/part14.rs index ae13e01..e16837c 100644 --- a/src/part14.rs +++ b/src/part14.rs @@ -38,7 +38,7 @@ pub fn sort(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 both of them as exclusive, mutable slices. + //@ Since the two slices don't overlap, there is no aliasing and we can have both of them as unique, 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 diff --git a/src/part15.rs b/src/part15.rs index 47952d6..79fd9b4 100644 --- a/src/part15.rs +++ b/src/part15.rs @@ -120,7 +120,7 @@ pub fn main() { //@ 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 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 mutable references. So in terms of concurrency, sending a mutable, exclusive reference is very much like +//@ the contents of two mutable references. So in terms of concurrency, sending a mutable, unique 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 reference? Without interior mutability, it would always be all-right to send such values. diff --git a/src/part16.rs b/src/part16.rs index 748e7d9..efa48a5 100644 --- a/src/part16.rs +++ b/src/part16.rs @@ -123,7 +123,7 @@ impl LinkedList { //@ 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 (exclusively) borrows a linked list. This has no +//@ it that in terms of ownership, this type actually (uniquely) 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 { diff --git a/workspace/src/part04.rs b/workspace/src/part04.rs index 2a3a648..e0d522e 100644 --- a/workspace/src/part04.rs +++ b/workspace/src/part04.rs @@ -44,7 +44,7 @@ fn shared_ref_demo() { println!("The first element is: {}", *first); } -// ## Exclusive, mutable references +// ## Unique, mutable references fn vec_inc(v: &mut Vec) { for e in v.iter_mut() {