Ownership, Reference, Borrow and Lifetime
- Each value has a variable called owner
- There can only be one owner at a time
- When the owner goes out of scope, the value will be dropped
Move semantics
// scope changes
{}
// for non-primitive data types, assign is move
let x = "string";
let y = x; // y and x both stay
let x = String::from("string");
let y = x; // x is moved to y and no longer in scope
// deep copy
let z = x.clone();
// passing a var to a function will move
// returning from a fn also move
let s = String::from("test");
pass_to_fn(s); // s is now out of scope
References
- A reference represents a borrow of some owned value.
- It is a pointer that is assumed to be aligned, not null, and pointing to memory containing a valid value of
T.
Inherited mutability
- Shared reference (aliasing)
&Tborrows from valueT, immutable, unlimited - Mutable reference (mutability)
&mut Tborrows and can mutate valueT, only one is allowed &mut Treferences can be freely coerced into&Treferences with the same referent type- Field access, method calling, and indexing work the same (between
&TandT) - Comparison operators transparently defer to the referent’s implementation
- e.g.,
&x == &ymeansx == y std::ptr::eq(&x, &y)compares addresses, not borrowed value
- e.g.,
let s1 = String::from("hello");
let s_ref = &s1;
// * to dereference; can use multilevels
// mut references
let mut s = String:from("hello");
let r1 = &mut s;
// ref ends after its use
let mut s = String::from("hello");
let r1 = &s;
println!("{}", r1); //used
let r2 = &mut s; // this is okay
println!("{}", r2);
println!("{}", r1): // this is not okay
// implicit dereference by `.` operator
let mut v = vec![123, 456];
v.sort(); // (&mut v).sort()
let s = SomeStruct::new();
let a = &s;
a.some_field; // a is implicitly deref to s
// implicit deref with comparing operatros
let x = 10;
let y = 10;
let rx = &x;
let ry = &y;
assert!(rx == ry);
// this can be as many levels as needed
let rrx = ℞
let rry = &ry;
assert!(rrx == rry);
// this would not pass
assert!(rx == rry);
// use this
assert!(rx == *rry);
// use `std::ptr::eq` to compare pointer addresses
assert!(!std::ptr::eq(rx, ry));
// implicit deref with atithmetic operators for one level
let r = &6;
assert_eq!(r + &2, 8);
Interior mutability
- Shareable mutable containers
- Introducing mutability inside of something immutable (
Struct, orRc<T>) - Implementation details of logically-immutable methods.
- Mutating implementations of Clone.
- Introducing mutability inside of something immutable (
- For thread safe, use
Mutex<T>andRwLock<T> Cell<T>implements interior mutability by moving values in and out of theCell<T>- To use references instead of values, one must use the
RefCell<T>type, acquiring a write lock before mutating.
Cell<T>
pub struct Cell<T: ?Sized> {
value: UnsafeCell<T>,
}
// T: Sized
fn new(value: T) -> Cell<T>
fn set(&self, val: T) // original value dropped
fn swap(&self, other: &Cell<T>)
fn replace(&self, val: T) -> T
fn into_inner(self) -> T
// T: Copy
fn get(&self) -> T
// T: Default
fn take(&self) -> T // return T, leaving default in place
RefCell<T>
pub struct RefCell<T: ?Sized> {
borrow: Cell<BorrowFlag>,
// Stores the location of the earliest currently active borrow.
// This gets updated whenever we go from having zero borrows
// to having a single borrow. When a borrow occurs, this gets included
// in the generated `BorrowError/`BorrowMutError`
#[cfg(feature = "debug_refcell")]
borrowed_at: Cell<Option<&'static crate::panic::Location<'static>>>,
value: UnsafeCell<T>,
}
// All T: Sized
fn new(value: T) -> RefCell<T>
fn into_inner(self) -> T
fn replace(&self, t: T) -> T
fn replace_with<F>(&self, f: F) -> T
where
F: FnOnce(&mut T) -> T,
fn swap(&self, other: &RefCell<T>)
// T: Default
fn take(&self) -> T
// T: ?Sized
fn borrow(&self) -> Ref<'_, T>
fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError>
fn borrow_mut(&self) -> RefMut<'_, T>
fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError>
// Ref and RefMut both impl Deref<Target=T>
let shared_map: Rc<RefCell<_>> = Rc::new(RefCell::new(HashMap::new()));
// Create a new block to limit the scope of the dynamic borrow
{
let mut map: RefMut<_> = shared_map.borrow_mut();
map.insert("africa", 92388);
map.insert("kyoto", 11837);
map.insert("piccadilly", 11826);
map.insert("marbles", 38);
}
// Note that if we had not let the previous borrow of the cache fall out
// of scope then the subsequent borrow would cause a dynamic thread panic.
// This is the major hazard of using `RefCell`.
let total: i32 = shared_map.borrow().values().sum();
println!("{}", total);
Choose move or reference
- Use references where full ownership is not required
- Use fewer long-lived values
- Duplicate value
Clone: may be expensive, need.clone()and can be diff than originalCopy: fast, implicit, always identical
- Wrap data with
Rc<T>,RefCell<T>, orArc<T>andMut<T>
Copy and Clone Trait
- types that implement
std::marker::Copytrait- integer
- Boolean
- floating
- char
- tuples containing primitive types
Copyis a subtrait ofstd::clone::CloneCopyis implicit;Copycannot be reimplemented- A type that impl
Copycannot implDrop
- A type that impl
Cloneis explicit and needs to call.clone(); can be reimplemented- Derivable
#derive[Copy, Clone]or#derive[Clone] - When
Clonecannot be derived, manually implement its.clone()method
// Copy
pub trait Copy: Clone { }
// Clone
pub trait Clone {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) { ... }
}
// manually implement Clone
struct Generate<T>(fn() -> T);
impl<T> Copy for Generate<T> {}
impl<T> Clone for Generate<T> {
fn clone(&self) -> Self {
*self
}
}
Borrow
- A module for working with borrowed data
- Trait
std::borrow::Borrow- Required method
.borrow() -> &Borrowed - immutably borrows from an owned value
- e.g.
Box<T>can be borrowed asT - e.g.
Stringcan be borrowed asstr
- Required method
- Comparing
Borrowandstd::convert::AsRefBorrowhas a blanket impl for anyTand can be used to accept either a ref or a valueBorrowrequires thatHash,EqandOrdfor borrowed value are equivalent to those of the owned value.- If only to borrow a single field of a struct, use
AsRef - If generic code needs to work for all types that can provide a ref to related type
T, useAsRef<T>
pub trait Borrow<Borrowed>
where
Borrowed: ?Sized,
{
fn borrow(&self) -> &Borrowed;
}
- Trait
std::borrow::BorrowMutfor mutably borrowing data- required method
.borrow_mut() -> &mut Borrowed
- required method
pub trait BorrowMut<Borrowed>: Borrow<Borrowed>
where
Borrowed: ?Sized,
{
fn borrow_mut(&mut self) -> &mut Borrowed;
}
// example
use std::borrow::BorrowMut;
fn check<T: BorrowMut<[i32]>>(mut v: T) {
assert_eq!(&mut [1, 2, 3], v.borrow_mut());
}
let v = vec![1, 2, 3];
check(v);
- Trait
std::borrow::ToOwned(Prelude)Cloneworks from&TtoTToOwnedworks from&Selfto&Self::Ownedsuch as&strtoString- Requires
.to_owned() -> Self::Owned - Provides
.clone_into(&mut Self::Owned)
// definition
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
fn clone_into(&self, target: &mut Self::Owned) { ... }
}
// example
let s: &str = "a";
let ss: String = s.to_owned();
let v: &[i32] = &[1, 2];
let vv: Vec<i32> = v.to_owned();
Cow
- A clone-on-write smarter pointer
- Either a reference or an owned value
- Impl
Derefso can call methods onB - Impl
Intoto construct from&BorB
enum Cow<'a, B: ?Sized>
where B: ToOwned
{
Borrowed(&'a B),
Owned(<B as ToOwned>::Owned),
}
// Cow provides `From` and `Into` converstions from both `String` and `&str`
// this extracts the owned string, clones the string if it is not already owned
impl<'a> From<Cow<'a, str>> for String
pub fn from(s: Cow<'a, str>) -> String
// If the string is not owned...
let cow: Cow<str> = Cow::Borrowed("eggplant");
// It will allocate on the heap and copy the string.
let owned: String = String::from(cow); // or `cow.into()`
assert_eq!(&owned[..], "eggplant");
// converts a `String` ref into a `Borrowed` variant; string is not copied
impl<'a> From<&'a String> for Cow<'a, str>
pub fn from(s: &'a String) -> Cow<'a, str>
let s = "eggplant".to_string();
assert_eq!(Cow::from(&s), Cow::Borrowed("eggplant"));
// converts a `String` to an `Owned` variant; string is not copied
impl<'a> From<String> for Cow<'a, str>
pub fn from(s: String) -> Cow<'a, str>
let s = "eggplant".to_string();
let s2 = "eggplant".to_string();
assert_eq!(Cow::from(s), Cow::<'static, str>::Owned(s2));
// Common use: a fn that may return a `&str` or an owned `String`
// most arms returns a `&str` and will be converted `Into` a `Cow`
// last arm converts a `String`
use std::path::PathBuf;
use std::borrow::Cow;
fn describe(error: &Error) -> Cow<'static, str> {
match *error {
Error::OutOfMemory => "out of memory".into(),
Error::StackOverflow => "stack overflow".into(),
Error::MachineOnFire => "machine on fire".into(),
Error::Unfathomable => "machine bewildered".into(),
Error::FileNotFound(ref path) => {
format!("file not found: {}", path.display()).into()
}
}
}
// Deref allows it to behave like B
println!("Disaster has struck: {}", describe(&error));
// methods
.to_mut() // to a mutable ref
.into_owned() // into owned value, consumes Cow
Lifetime
- About connecting the lifetimes of various parameters and return values of fn
- To ensure reference does not outlive its referent
&T // a ref
&'a T // a reference with an explicit lifetime `a`
&'a mut T // a mutable ref with an explicit lifetime `a`
foo<'a, 'b> // foo can be a function or impl, or method, or a Type
<'_> // anonymous lifetime
<'static> // static lifetime (last throughout the program)
let s: &'static str = "hello world";
static NUM: i32 = 18;
Elision: inferred lifetime
-
When not necessary to note lifetime in function signature
- The function doesnt return a reference
- There is exactly one reference input parameter
- The function is a method taking
&selfor&mut selfas first parameter
-
If a function does not return a type that includes reference, lifetime annotation is not necessary
fn no_return(x: &i32, y: &str) { ... }
fn no_return(x: &i32, y: &str) -> bool { ... }
- If a function that returns a reference, it needs to take input parameter(s) that has reference. Otherwise, the returning reference would come from the function itself, and it will be dangled reference
// not compile
fn bad_fn(x: i32, y: i32) -> &i32 { ... }
- If a function that returns a reference has only one input parameter, which is a reference, the lifetime is inferred
fn one_input(x: &i32) -> &i32 { ... }
// same
fn one_input<'a>(x: &'a i32) -> &'a i32 { ... }
- If a function that returns a reference has multiple input parameters, but one of them is
&selfor&mut self, the returning reference lifetime is the same as&self
fn has_self(&self, x: &i32) -> &i32 { ... }
// same
fn one_input<'a>(&self, x: &'a i32) -> &'a i32 { ... }
- If a function that returns a reference has multiple input paramters, and none of them is
&selfor&mut self, lifetime needs explicitly annoated
fn two_inputs<'a, 'b>(x: &'a i32, y: &'b i32) -> &'a i32 { ... }
- Struct/enum that has ref in the fields need to explicitly annotated
#[derive(Debug)]
struct NamedBorrowed<'a, 'b> {
x: &'a i32,
y: &'b str,
}
// An enum which is either an `i32` or a reference to one.
#[derive(Debug)]
enum Either<'a> {
Num(i32),
Ref(&'a i32),
}
// traits
// Annotate lifetimes to impl.
impl<'a> Default for Borrowed<'a> {
fn default() -> Self {
Self {
x: &10,
}
}
}
// bounds
// T: 'a - all ref in T must outlive lifetime 'a
// T: Trait + 'a - all T must impl Trait and all ref in T must outlive 'a
#[derive(Debug)]
struct Ref<'a, T: 'a>(&'a T);