Rust :: Ownership and Lifetime

Ownership, Reference, Borrow and Lifetime

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

Inherited mutability

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

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

Copy and Clone Trait

// 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

pub trait Borrow<Borrowed> 
where
    Borrowed: ?Sized, 
{
    fn borrow(&self) -> &Borrowed;
}
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);
// 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

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

&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

fn no_return(x: &i32, y: &str) { ... }
fn no_return(x: &i32, y: &str) -> bool { ... }
// not compile
fn bad_fn(x: i32, y: i32) -> &i32 { ... }
fn one_input(x: &i32) -> &i32 { ... }
// same
fn one_input<'a>(x: &'a i32) -> &'a i32 { ... }
fn has_self(&self, x: &i32) -> &i32 { ... }
// same
fn one_input<'a>(&self, x: &'a i32) -> &'a i32 { ... }
fn two_inputs<'a, 'b>(x: &'a i32, y: &'b i32) -> &'a i32 { ... }
#[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);