Ownership

And

Borrowing

By: Davis Silverman

Who is This Guy?

  • Student / Technologist at The Humangeo Group
  • Rust enthusiast for over 2 years.
  • Knows about Ownership and Borrowing

What is Rust?

  • Modern language, built on decades of PL research
  • Safety of Garbage Collection, without the overhead
  • Concurrent, built on a great type system

Why Use Rust?

  • As a safe alternative to C, without sacrificing performance
  • To write performance sensitive bits of code through FFI
  • To feel superior to our coworkers

Copy and Clone

  • Copy data does not move its data
    • Copy data is generally any Plain Old Data type
  • Clone is necessary when you do not want to move
    • Most every time implements Clone

Rust Pointer Types

  • Reference Types: &, &mut
  • Library Types: Box<T>, Rc<T>
  • Raw Pointers: *const, *mut
  • Interior Mutability: Cell<T>, RefCell<T>

Box<T>

  • Used to own its data
  • Just a single owner allowed
fn main() {
    let x: Box<i32> = Box::new(64); // allocates memory for one i32
} // x is deallocated here

Rc<T>

  • Also an owned Data Structure
  • Multiple owners allowed
  • Runtime overhead
use std::rc::Rc;

fn main() {
    let mut first = Rc::new(132);
    let sec = first.clone(); // cheap to clone, bumps reference count
    *Rc::make_mut(&mut first) *= 3; // Copy on Write
    let third = sec.clone();
    
    println!("{:?} {:?} {:?}", first, sec, third);
}

Raw Pointers

  • Completely Unsafe to dereference
  • Great for FFI or low level datastructures
fn main() {
    let x = 5;
    let raw = &x as *const i32;

    let mut y = 10;
    let raw_mut = &mut y as *mut i32;

    println!("the value of raw_mut is: {:?}.", unsafe {*raw_mut});
}

Cell<T> and RefCell<T>

  • Interior Mutability
  • Used when you want transparent mutability in a datastructure
use std::cell::{Cell, RefCell};

fn main() {
    let cell = Cell::new(36);
    cell.set(cell.get() * 2);

    let rcell = RefCell::new(37);
    *rcell.borrow_mut() *= 3;
    
    println!("cell: {:?} - rcell: {:?}", cell, rcell);
}

What is Ownership?

  • Making sure resources can not be invalidated
  • Ensures resources allocated and freed only once
  • Obviates the need for garbage collection
  • (semi) Affine types

Ownership

fn main() {
    let v = vec![1,2,3]; // this Vec is owned by `v`
    let v2 = v; // ownership of the Vec is transferred to v2 
    // println!("{:?}", v); // this will fail if uncommented.
}

Deterministic Destruction

fn main() {
    let v = vec![1, 2, 3]; // this Vec is owned by `v`

    println!("vector: {:?}", v); // actually borrows `v`
    // v will be 'Dropped' here. at the end of the scope
}

What is Borrowing?

  • Temporarily having read permissions to a resource
  • Temporarily having write permissions to a resource
  • Each borrow is for an exact section of code, the lifetime of the borrow

Shared Borrows

#[derive(Debug)]
struct Thing {
    x: i32,
}

fn borrow_it(t: &Thing) {
    println!("the value is {:?}!", t.x);
}

fn main() {
    let t = Thing {x: 865};
    
    borrow_it(&t);
    
    println!("Can still use it: {:?}!", t);
}
  • ∞ uses
  • read-only access

Exclusive Borrows

fn change_it(t: &mut Thing) {
    t.x *= 11;
}

fn main() {
    let mut t = Thing { x: 3 };
    change_it(&mut t);
    println!("first try: {:?}", t);
    
    let copy = &mut t;
    
    // println!("CANT ACCESS t {:?}", t);
    
    change_it(copy);
    
    println!("can access the copy: {:?}", copy);
}
  • Single use during its lifetime
  • Read and write access

Named lifetimes

#[derive(Debug)]
enum Token<'a> {
    Word(&'a str),
    Other(&'a str)
}

fn tokenize_str<'a>(text: &'a str) -> Vec<Token<'a>> {
    let mut result = vec![];
    for cap in Regex::new(r"(\w+)|(\W+)").unwrap().captures_iter(text) {
        let token = match cap.at(1) {
                        Some(word) => Token::Word(word),
                        None => Token::Other(cap.at(2).unwrap())
                    };
        result.push(token);
    }
    result
}

fn main() {
    let tokens = tokenize_str("the-sinistersnare");
    println!("{:?}", tokens);
}

http://www.randomhacks.net/2014/09/19/rust-lifetimes-reckless-cxx/

Mutliple lifetimes

fn search<'a, 'b>(needle: &'a str, haystack: &'b str) -> Option<&'b str> {
    let len = needle.len();
    if haystack.chars().nth(0) == needle.chars().nth(0) {
        Some(&haystack[..len])
    } else if haystack.chars().nth(1) == needle.chars().nth(0) {
        Some(&haystack[1..len+1])
    } else {
        None
    }
}
fn main() {
    let haystack = "hello, Rust DC!";
    let res;
    {
        let needle = String::from("ello");
        res = search(&needle, haystack);
    }
    match res {
        Some(x) => println!("found {}", x),
        None => println!("nothing found")
    }
}

Lifetime Elision

fn print(s: &str);                                      // elided
fn print<'a>(s: &'a str);                               // expanded

fn debug(lvl: uint, s: &str);                           // elided
fn debug<'a>(lvl: uint, s: &'a str);                    // expanded

fn substr(s: &str, until: uint) -> &str;                // elided
fn substr<'a>(s: &'a str, until: uint) -> &'a str;      // expanded

fn frob(s: &str, t: &str) -> &str;                      // ILLEGAL
fn frop<'a,'b>(s: &'a str, t: &'b str) -> &'? str

fn args<T: ToCStr>(&mut self, args: &[T]) -> &mut Command
fn args<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command
  • You do not always need to type lifetime parameters
  • lifetimes can be elided, never inferred

Pop Quiz

Does it run?

fn as_str(data: &u32) -> &str {
    let s = format!("{}", data);
    &s
}

Does it run?

fn main() {
    let mut data = vec![1, 2, 3];
    let x = &data[0];
    data.push(4);
    println!("{}", x);
}

Does it run?

struct Foo;

fn main() {
    let mut a = Foo;
    let mut b = Foo;
    let mut x = &mut a;
    let y = &*x;
    x = &mut b;
}

Safety

ITERATOR INVALIDATION

let mut v = vec![1, 2, 3];

for i in &v {
    println!("{}", i);
    v.push(34);
}
  • Mutating an iterator while reading from it
  • Easy to do accidentally in C++
  • Cant happen in Rust

USE AFTER FREE

let y: &i32;
{
    let x = 5;
    y = &x;
}

println!("{}", y);
  • Accessing a resource after it has been deleted
  • Cant happen in Rust  

Advanced Lifetimes

'static

static FOO: &'static i32 = &2_300_200;
static BAR: &'static str = "FOREVER";


fn main() {
    let loc: &'static str = "ALSO FOREVER";

    println!("FOO: {:?} - BAR: {:?} - loc: {:?}", FOO, BAR, loc);

}
  • Lifetime of the entire program
  • baked into the data segment

Subtyping

fn get_it<'a, 'b: 'a>(_: &'a str) -> &'b str {
    // string literals are `&'static str`s
    // &'static str is a subtype of any 'a
    "hello"
}

fn main() {
    let t = String::from("unused");
    println!("{:?}", get_it(&*t));
}
  • The fact that some liftimes are greater than others
  • 'a: 'b means that 'a contains 'b

Thanks!

  • Find me @Sinistersnare
  • Give talks!
  • Questions?

OwnershipAndBorrowing

By Davis Silverman

OwnershipAndBorrowing

  • 1,390