Ownership in the type system

Ben Clifford

London Haskell, June 2018

benc@hawaga.org.uk

Press 's' for speaker notes

What is Rust?

  • closures
  • traits / typeclasses
  • parametric polymorphism
  • concurrency
  • pattern matching
  • single-assignment variables
  • nice macros

void f() {
  r = malloc(10);
  mutate(r);
  free(r);
}

do
  r <- hOpenFile "foo" WriteMode
  hPutStrLn r "hello world"
  hClose r 

use-after-free


void f() {
  r = malloc(10);
  free(r);
  mutate(r);
}

do
  r <- hOpenFile "foo" WriteMode
  hClose r 
  hPutStrLn r "hello world"

resource leak


void f() {
  r = malloc(10);
  mutate(r);
}

do
  r <- hOpenFile "foo" WriteMode
  hPutStrLn r "hello world"

let r = FooConstructor
    in process r

withFile "myfile.txt" WriteMode $
  \r -> hPutStrLn r "hello world"

void f()
{
  int x;
  mutate(&x)
}

do 
  r' <- withFile "myfile.txt" WriteMode $
          \r -> return r

int * f()
{
  int x;
  return &x;
}

int f(int *r)
{
  return 7;
}

f :: (Handle, Handle) -> IO ()
f (_,b) = hClose b

void f() {
  r = malloc(10);
  mutate(r);
  free(r);
}

do
  r <- hOpenFile "foo" WriteMode
  hPutStrLn r "hello world"
  hClose r 

fn main()
{
  let r = vec![1, 2, 3];
  process(&r);
}


fn main()
{ 
  let r = vec![1, 2, 3];
  process(&r);
  drop(r);
}

fn main()
{
  let r = vec![1, 2, 3];
  process(&r);
  drop(r);
  process(&r);
}
5 |   drop(r);
  |        - value moved here
6 |   process(&r);
  |            ^ value used here after move

fn main()
{
  let r = vec![1, 2, 3];
  process(&r);
  process(&r);
}

fn process(s : &Vec<i32>) {
  println!("vector size: {}", (*s).len());
}
$ ./a 
vector size: 3
vector size: 3

fn main()
{
  let r = vec![1, 2, 3];
  process(&r);
  process(&r);
}

fn process(s : &Vec<i32>) {
  println!("vector size: {}", s.len());
  drop(*s)
}
error[E0507]: cannot move out of borrowed content
  --> a.rs:10:8
   |
10 |   drop(*s)
   |        ^^ cannot move out of borrowed content

fn main()
{
  let r = vec![1, 2, 3];
  process(r);
  process(r);
}

fn process(s : Vec<i32>) {
  println!("vector size: {}", s.len());
} // release happens here
error[E0382]: use of moved value: `r`
 --> a.rs:5:11
  |
4 |   process(r);
  |           - value moved here
5 |   process(r);
  |           ^ value used here after move
  |
  = note: move occurs because `r` has type `std::vec::Vec<i32>`,
  which does not implement the `Copy` trait

fn main()
{
  let r = 10;
  process(r);
  process(r);
}

fn process(s : i32) {
  println!("integer is: {}", s);
}
$ ./b 
integer is: 10
integer is: 10

fn main()
{
  let r = vec![1, 2, 3];
  let q = vec![1, 2, 3, 4];
  let l = longest(&q, &r);
  process(l);
}

fn longest(s : &Vec<i32>, t : &Vec<i32>) -> &Vec<i32> {
  if (*s).len() > (*t).len() {
    return s;
  } else {
    return t;
  }
}
error[E0106]: missing lifetime specifier
 --> c.rs:9:45
  |
9 | fn longest(s : &Vec<i32>, t : &Vec<i32>) -> &Vec<i32> {
  |                       expected lifetime parameter ^
  |
  = help: this function's return type contains
    a borrowed value, but the signature does not say
    whether it is borrowed from `s` or `t`

fn main()
{
  let r = vec![1, 2, 3];
  let q = vec![1, 2, 3, 4];
  let l = longest(&q, &r);
  process(&l);
}

fn longest <'a> (s : &'a Vec<i32>, t : &'a Vec<i32>)
     -> &'a Vec<i32> {
  if (*s).len() > (*t).len() {
    return s;
  } else {
    return t;
  }
}

use std::rc::Rc;

fn main()
{
  let r = Rc::new(vec![1, 2, 3]);
  let q = r.clone();
  process(&r);
  process(&q);
  drop(r);
  process(&q);
  drop(q);
}

fn process(s : &Vec<i32>) {
  println!("vector size: {}", (*s).len());
}

use std::sync::Mutex;

fn main()
{ 
  let r = Mutex::new(vec![1, 2, 3]);
  { 
    let r1 = &r.lock().unwrap();
    process(r1);
  }
  { let r2 = &r.lock().unwrap();
    process(r2);
  }
}
  • accessing serialised data without copying it
  • sockets with type level state
  • pure bindings to impure APIs