about
04/28/2022
RustBites - Ownership
Rust Bite - Ownership
rules, exercises, conclusions
1. Ownership Rules:
- There is only one owner for every data item
- Ownership can be transferred with move operations
- Ownership can be borrowed by creating references
- Any number of readers may have access to a data value simultaneously
- Writers get exclusive access to a value - no other readers or writers
-
Any variable bound to a value with no mut qualifier is a reader.
-
Owner:
let s = String::from("a string"); -
References to the data:
let t = &s;
-
Owner:
-
Any variable bound to a value with a mut qualifier is a writer.
-
Owner:
let mut u = String::from("a string"); -
References to the data:
let v = &mut u;
-
Owner:
-
References are called borrows because they block the owner u's
ability to mutate its owned resource.
- While mutable reference, v, is active, owner, u, is not allowed to mutate its owned value - that's borrowed by v.
- There can only be one mutable reference, since the ability to write is exclusive. That's why a mutable reference blocks the the owner's ability to mutate.
-
References are active from the time they are created (with a let statement)
until they go out of scope.
- References can be dropped. What that does is interesting, but not very useful.
- Dropping an immutable reference does nothing. reference is valid after the drop.
- Dropping a mutable reference moves the reference, but not the referend, to Drop's stackframe. That makes the mutable reference unavailable (been moved).
-
Nuances:
- No practical process can exactly divide all programs into safe and not-safe code. The borrow-checker is conservative. If it can't prove that a program is safe it will not build the program.
-
The rules stated above have some qualifications that allow more programs to build safely. These
are known by the Rust community as non-lexical lifetime rules - see
Rust 2018 edition-guide
- An expression with non-mutable reference to x, inside the scope where a mutable reference to x is defined, will compile if no other expression argument supports mutation of x.
- A non-mutable reference to x may be defined and used in the scope where a mutable reference to x is defined, provided that the mutable reference is not used after the non-mutable reference is declared.
- These rule relaxations still maintain the "no aliases when mutating" invariant.
- There is one other case: It may be that during program operation a variable will never be mutated at the same time that an alias to the variable exists, even though the scope rules above are violated. In this case a RefCell<T> may be created. That appears to the compiler to be non-mutable, but a mutable reference can be extracted from it. The RefCell tracks references and mutation in time. If our reasoning about the timing of mutation and aliasing is incorrect, the RefCell will panic and program operation terminates, preventing undefined behavior.
Examples of Ownership
- In the first block, Blk #1, in main we see two active readers - O.K.
- In the second block, Blk #2, we see one writer - O.K.
- In the last uncommented block, before the function call, Blk #3, we see reference v borrowing u's ability to mutate, so there is only one writer - O.K.
- In the function we see a reference to u mutating. This is not a violation because the reference, &mut String, goes out of scope at the end of the function.
- The commented blocks will be discussed in the next examples.
Example: First Commented Error Block
Example: Second Commented Error Block
Example: Third Commented Error Block
2. Exercises:
In order to build and run with cargo from the Visual Studio Code terminal you need to open
VS Code in the package folder for the code you want to build and run. That's the folder where
the package cargo.toml file resides.
-
Create an instance of a Vec<String>. Show that you can mutate using the owner identifier
and using a reference.
- Verify that it was mutated as expected.
- Pass a reference to your Vec to a function that displays the Vec contents.
-
Add to the Code of the previous exercise code that attempts to violate ownership
rules in as many unique ways as possible, but be DRY.
- Write code that fixes each problem based on the compiler error messages