Rust

The memory safe and efficient language of the future

@Manishearth

Prelude: C++ problems

  • Dangling pointers
  • Iterator invalidation
  • Thread safety

Dangling pointers


int* abc() {
	int x = 1;
	return &x;
}
	

Iterator invalidation


// Create the vector
vector<int> x;
x.push_back(1);
x.push_back(2);
x.push_back(3);

// Get an iterator
vector<int>::iterator itr = x.begin();

int j=0;
for (itr; itr !=x.end(); itr++) {
	x.push_back(j); // Uh oh!
	j++;
	cout << j << " .. ";
}
	

From a Stack Overflow question, by user620189

Why is this a problem?

  • SEGFAULT, SEGFAULT, SEGFAULT (Or: runtime failures are bad)
  • Extremely common source of security bugs in browsers and similar applications
  • Existing solutions for safety are slow and usually require a separate runtime with a GC

Rust basics

If, loops, functions


if a > 1 {
	println!("{:?}", a);
}

let mut i: i32 = 0;
loop {
	if i > 10 { break; }
	i += 1;
	println!("{:?}", i);
}

fn multiply(x: i32, y: i32) -> i32 {
	return x*y // The return statement is unnecessary, this will be explained later
}
	

Control flow

Semicolons have meaning!


let b = 3i32;
let a = if b > 1 {
	let mut c = b - 5;
	c = c * b; // Removing these two semicolons cause errors
	c // This "returns" the value of c, due to the lack of semicolon
} else {
	1i32
};

fn greater(a: i32, b: i32) -> i32 {
	if a > b {
		  a
	} else {
		  b
	}
}
	

Matching

Switch-case just got a makeover


let number = 5u32;
let size = match number {
	0 => "none",
	2 | 3 => "tiny",
	4...7 => "small",	
	8...20 => "medium",
	_ => "large"
};
// Multiple statements in a match work!
match number {
	2 | 8 | 9 => {
	  let mut c = 1;
	  c += number;
	  println!("The incremented number is {}", c);
	},
	_ => println!("We don't have the ability to increment this number, sorry")
}
	

Destructuring


let pair = (4u32, 5u32);
let (a, b) = pair; // a=4, b=5
let (b, a) = (a, b); // Swap!
let smaller = match pair {
	(x, y) if x < y => x,
	(_, y) => y
};

match pair {
	(0, 0) => println!("Origin"),
	(0, y) => println!("Y-axis, coordinate {}", y),
	(x, 0) => println!("X-axis, coordinate {}", x),
	(x, y) => {
	  let distance = ((x*x + y*y) as f32).sqrt();
	  println!("Point is ({}, {}), and {} units from origin", x, y, distance);
	}
};
	

Datatypes

Structs


struct Point {
	pub x: i32,
	pub y: i32
}

let p = Point {x: 0, y: 0};
println!("X coordinate is {}", p.x)
	

Enums


enum Shape {
	Circle(Point, u32),
	Rectangle(Point, Point)
}

// Real world example
enum Option<T> {
	Some(T),
	None
}

let origin = Point {x: 0, y: 0};
let circ = Shape::Circle(origin, 10);
	

Matching!


let perimeter = match shape {
	Circle(_, r) => 2*pi*r,
	Rectangle(p1, p2) => 2*abs(p2.x - p1.x) + 2*abs(p2.y - p1.y)
}

// But wait, we can match on structs too!
let perimeter = match shape {
	Circle(_, r) => 2*pi*r,
	Rectangle(Point {x: x1, y: y1}, Point {x: x2, y: y2}) => 
	  2*abs(x2 - x1) + 2*abs(y2 - y1)
}

// Patterns nest pretty well
match point {
	Point {x: 2...6, y: -1...5} => println!("I like this point"),
	_ => println!("I do not like this point")
}
	

Generics


enum Option<T> {
	Some(T),
	None
}

fn maybe_sqrt(x: i32) -> Option<u32> {
	if x >= 0 {
	  Some(sqrt(x) as u32)
	} else {
	  None
	}
}
	

Implementations


impl Point {
	fn distance(&self) -> f32 { // Called as point.distance()
	    ((self.x*self.x + self.y*self.y) as f32).sqrt()
	}
	fn random() -> Point { // Called as Point::random()
	  Point {
	    x: 4, // Chosen by fair dice roll
	    y: 4  // Guaranteed to be random
	  }
	}
}

	

Traits


// Haskell's type classes, anyone?
trait Pointy {
        fn poke(&self, at: &str);
        // One can also give full function bodies here
}

impl Pointy for Point {
        fn poke(&self, at: &str) {
                println!("Poked {}", at);
        }
}

fn poke_forever<T: Pointy>(pointy: T, at: &str) {
        loop {
         pointy.poke(at)
        }
}

	

Memory
management!

The good stuff

Moves and copies


let x = 5i8;
let y = x;
println!("x is {:?}", x); // y was copied from x

let x = vec![1u8, 2u8, 3u8]; // A vector
let y = x; // the vector was "moved"
println!("y is {:?}", y);
// Will not work, the vector is "owned" by y
// println!("x is {}", x)

fn abc(x: Vec<u8>) {
    // do something
}
let myvec = vec![1u8, 2u8, 3u8];
abc(myvec); // myvec moved into function, cannot be used after this
	

Borrowing


let x = vec![1u8, 2u8, 3u8];
let y = &x; // the vector was "borrowed"
let c = x.clone(); // Explicit copy
println!("x is {:?}", x);
println!("y is {:?}", *y);

fn abc(x: &Vec<u8>) {
    // do something
}
let myvec = vec![1u8,2u8,3u8];
abc(&myvec); // Passes a borrowed reference
// Still can use myvec here
	

Borrowing & mutability


let mut x = vec![1u8,2u8,3u8];
{
  let y = &x; // the pointer was "borrowed"
  // x.push(1); // Not allowed, x is currently borrowed and cannot be mutated
  // y.push(1); // Not allowed, y is not a mutable reference
}
x.push(1); // The borrow was "returned", we can mutate now

let mut x = vec![1u8,2u8,3u8];
{
  let y = &mut x; // the pointer was "borrowed", mutably
  // x.push(1); // Still not allowed, x is currently borrowed and cannot be mutated
  // println!("x is {}", x) // also not allowed, y is mutating this
  y.push(1); // Allowed, y is a mutable reference
}
x.push(1) // The borrow was "returned", we can mutate now
	

Ownership and implementations


struct Polygon {points: Vec<Point>};
impl Point {
	// This one moves
	fn draw_move(self) {
	    // ...
	}
	// This one borrows
	fn draw_borrow(&self) {
	    // ...
	    // after calling p.draw_borrow() I can still use p
	}
	// This one borrows mutably
	fn draw_borrow_mut(&mut self) {
	    // ...
	    // I can mutate self here
	}
}

// (*polygon).draw_borrow() dereference not necessary
	

The heap


let mut x = Box::new(1); // On the heap
*x = 2;

// Type Box<u32>
// Also gets moved, not copied

fn abc() {
	let x = Box::new(1); // malloc() happened
	// do stuff with x or *x
	// free() happened
}

fn def() -> Box<u32>{
	let x = Box::new(1); // malloc() happened
	// do stuff with x or *x
	x // x returned to outer owner	
}
	

Destructuring and binding by reference


let pair = (Box::new(1), Box::new(2));
let (a, b) = pair;
// The boxes were moved out of `pair`, cannot use it anymore

let pair = (Box::new(1), Box::new(2));
{
 let (ref a, ref b) = pair;
 // a, b are borrowed references now, so everything is fine
 // use `ref mut` for mutable references
}

// Works in match statements too!
let maybe_heap = Some(Box::new(1));
match maybe_heap {
	Some(ref x) => println!("{}", x),
	None => println!("No variable")
}
	

Strings and arrays


let mut fixed_len_vec = [1,2,3]; // Of type [u32, ..3]
fixed_len_vec[1] = 2;
let mut buffer = vec![1,2,3]; // Of type Vec<u32>
buffer.push(20); // Now it is of length 4!
let slice = &buffer[0..2]; // Of type &[u32]
let slice = &[1,2,3]; // Same

let owned = "Manish".to_string(); // of type String
let static_slice = "Manish"; // type `&'static str`
let slice = &owned[0..3]; // type &str . Basically a borrowed "view" into the String
	

Unsafe


extern crate core;
use std::{mem, ptr};
fn main() {
 let y = *dangle() + 1;
 // Segfault.
}

fn dangle() -> Box<u32> {
 unsafe {
  // Null pointers!
  let mut p: *mut int = ptr::null_mut();
  let b = Box::new(1u32); // Heap memory, will be deallocated at the end of this function
  ptr::write(p, *b);
  mem::transmute(p) // Converts to box and .. uh oh .. returns!
 }
}
	

More features

  • Thread safety (data races)
  • Powerful zero cost abstractions
  • Error handling via result types
  • Associated types and constants
  • Closures
  • Plugin ecosystem (custom syntax extensions and lints)

Library status

Resources

Thank you!

[Extra slides]

Thread safety


use std::sync::mpsc::*;
use std::thread::spawn;
let (tx, rx) = channel();

spawn(move || {
	tx.send(1); // works!
});

let x = rx.recv();

	

use std::sync::mpsc::*;
use std::thread::spawn;
let (tx, rx) = channel();

spawn(move || {
	let x = Box::new(1);
	tx.send(x); // works!
});

let x = rx.recv();

	

use std::sync::mpsc::*;
use std::thread::spawn;
let (tx, rx) = channel();

spawn(move || {
	let x = Box::new(1);
	tx.send(&x); // nope
});

let x = rx.recv();

	

use std::sync::mpsc::*;
use std::thread::spawn;
use std::rc::Rc;
let (tx, rx) = channel();

spawn(move || {
	let x = Rc::new(1);
	// refcounted pointer
	// not threadsafe
	tx.send(x.clone()); // nope
});

let x = rx.recv();

	
test.rs:18:1: 18:6 error: the trait `core::marker::Send` is not implemented for the type `alloc::rc::Rc<i32>` [E0277]
test.rs:18 spawn(move || {
           ^~~~~
test.rs:18:1: 18:6 note: `alloc::rc::Rc<i32>` cannot be sent between threads safely
test.rs:18 spawn(move || {