Type checking memory safety in Rust

Transferring ownership

let p = Box::new(42i32);
//case 1
let q = p; 
// p is moved to q;
// q will be reclaimed (freed) after the scoped defined 
//case 2
foo(p); 
// function call receives the ownership of p

Borrowed type

  • Immutable + Full sharing
let s1 = String::from("hello"); //ownership
let s2 = calculate_length(&s1);
println!("The length of `{}` is {}.", s1, len);

fn calculate_length(s: &String) -> usize {
  s.len() // why safe?: &String type ensures that s is immutable
}


// function signature
// pub fn len(&self) -> usize
// pub fn make_ascii_uppercase(&mut self) 

Ensuring pointer validity

  • Using after free
let p = Box::new(42i32);
let x = &p;
drop(p);

*x; // error!
  • Dangling pointer
fn dangle() -> &String {
  let f = String::from("");
  &f
} // freed when the scope ends; f is dropped

Mutably borrowed type

  • Mutable + sharing
fn change(mut some_string: String) -> String {
  some_string.push_str(" ");
  some_string
}
// mutable borrow sanitizes this code
fn change(mut some_string: &mut String) {
  some_string.push_str(" "); //ok
}
// function call
let mut s: String = String::from("hello");
change(&mut s);

Alias(borrow) XOR Mutation

  • mutating object with immutable borrow is prohibited.
  • mutation is ok if there is only no borrows

What Rust prevents

g = lock();   //  --
p = &g.data;  //    | lifetime of g
              //    | : critical section
drop(g);      //  --
do_something(*p) // rust compiler rejects this code
  • Some more programming features

    • slices and structs
    • smart pointers
    • first-class functions
    • parallel programming
  • lifetime-wise: lock > lockguard > data inside

// api.rs:109

pub struct LockGuard<'s, L: RawLock, T> {
  lock: &'s Lock<L, T>,  
  // lifetime(lock) > lifetime(lockguard)
  token: ManuallyDrop<L::Token>,
}