Rust 
Nicholas Matsakis 
Mozilla Research
What makes Rust different? 
C++ 
Haskell 
Java 
ML 
More Control More Safety 
Rust: Control and safety
Why Mozilla? 
Browsers need control. 
Browsers need safety. 
Servo: Next-generation 
browser built in Rust.
What is control? 
void example() { 
vector<string> vector; 
… 
auto& elem = vector[0]; 
… 
} 
Stack and inline layout. 
Lightweight references 
Deterministic destruction 
[0] 
string 
el…em 
vector 
data 
length 
capacity 
[…] 
[n] 
… 
‘H’ 
‘e’ 
… 
Stack Heap 
C++
Zero-cost abstraction 
Ability to define abstractions that 
optimize away to nothing. 
vector data 
length 
cap. 
[0] 
[…] 
data cap. 
‘H’ 
‘e’ 
[…] 
Not just memory layout: 
- Static dispatch 
- Template expansion 
- … Java
What is safety? 
void example() { 
vector<string> vector; 
… 
auto& elem = vector[0]; 
vector.push_back(some_string); 
cout << elem; 
} 
vector 
data 
length 
capacity 
[0] 
… 
[0] 
[1] 
elem 
Aliasing: more than 
one pointer to same 
memory. 
Dangling pointer: pointer 
to freed memory. 
C++ 
Mutating the vector 
freed old contents.
What about GC? 
No control. 
Requires a runtime. 
Insufficient to prevent related problems: 
iterator invalidation, data races.
void foo(Foo &&f, …) { 
vec.push_back(f); 
} 
C++ 
Gut it: 
void foo(const Foo &f, …) { 
use(f.bar); 
} 
Read it: 
void foo(Foo &f, …) { 
f.bar = …; 
} 
Write it: 
void foo(unique_ptr<Foo> f, …) { 
… 
} 
Keep it:
Definitely progress. 
! 
But definitely not safe: 
! 
- Iterator invalidation. 
- Double moves. 
- Pointers into stack or freed memory. 
- Data races. 
- … all that stuff I talked about before … 
! 
Ultimately, C++ mechanisms are unenforced.
The Rust Solution 
Codify and enforce safe patterns 
using the type system: 
! 
1. Always have a clear owner. 
2. While iterating over a vector, 
don’t change it. 
3. … 
! 
No runtime required.
Credit where it is due 
Rust borrows liberally from other great languages: 
C++, Haskell, ML/Ocaml, Cyclone, 
Erlang, Java, C#, … 
Rust has an active, amazing community. 
❤
The Rust type system
Observation 
Danger arises from… 
Aliasing Mutation 
Hides dependencies. Causes memory 
to be freed. 
{ auto& e = v[0]; … v.push_back(…); }
Three basic patterns 
fn foo(v: T) { 
… 
} 
Ownership 
fn foo(v: &T) { 
… 
} 
Shared borrow 
fn foo(v: &mut T) { 
… 
} 
Mutable borrow
Ownership! 
! 
n. The act, state, or right of possessing something.
Aliasing Mutation 
Ownership (T)
vec 
data 
length 
capacity 
1 
2 
fn give() { 
let mut vec = Vec::new(); 
vec.push(1); 
vec.push(2); 
take(vec); 
… 
} 
fn take(vec: Vec<int>) { 
// … 
} 
! 
Take ownership 
! 
of a Vec<int> 
!
Compiler enforces moves 
fn give() { 
let mut vec = Vec::new(); 
vec.push(1); 
vec.push(2); 
take(vec); 
vec.… 
push(2); 
} 
fn take(vec: Vec<int>) { 
// … 
} 
! 
! 
Error: ve! c has been moved 
Prevents: 
- use after free 
- double moves 
- …
Borrow! 
! 
v. To receive something with the promise of returning it.
Aliasing Mutation 
Shared borrow (&T)
Aliasing Mutation 
Mutable borrow (&mut T)
fn lender() { 
let mut vec = Vec::new(); 
vec.push(1); 
vec.push(2); 
use(&vec); 
… 
} 
fn use(vec: &Vec<int>) { 
// … 
} 
! 
! 
! 
1 
data 
length 
capacity 
vec 2 
vec 
“Shared reference 
to Vec<int>” 
Loan out vec
fn dot_product(vec1: &Vec<int>, vec2: &Vec<int>) 
elem1 
elem2 
sum 
-> int { 
let mut sum = 0; 
for (elem1, elem2) in vec1.iter().zip(vec2.iter()) { 
sum += (*elem1) * (*elem2); 
} 
return sum; 
} walk down matching indices 
elem1, elem2 are references into the vector 
1 
2 
3 
4 
5 
6 
* 
vec1 vec2
Why “shared” reference? 
fn dot_product(vec1: &Vec<int>, vec2: &Vec<int>) 
-> int 
{…} 
! 
fn magnitude(vec: &Vec<int>) -> int { 
sqrt(dot_product(vec, vec)) 
} 
two shared references to the 
same vector — OK!
Aliasing Mutation 
Shared references are immutable: 
fn use(vec: &Vec<int>) { 
vec.push(3); 
vec[1] += 2; 
} 
* 
Error: cannot mutate shared reference 
* Actually: mutation only in controlled circumstances
Mutable references 
fn push_all(from: &Vec<int>, to: &mut Vec<int>) { 
for elem in from.iter() { 
to.push(*elem); 
} 
} 
mutable reference to Vec<int> 
push() is legal
Mutable references 
fn push_all(from: &Vec<int>, to: &mut Vec<int>) { 
for elem in from.iter() { 
1 
2 
3 
to.push(*elem); 
} 
} 
from 
to 
elem 
1 
2 
3 
…
What if from and to are equal? 
fn push_all(from: &Vec<int>, to: &mut Vec<int>) { 
for elem in from.iter() { 
1 
2 
3 
from 
to 
elem 
1 
2 
3 
… 
1 
to.push(*elem); 
} 
} dangling pointer
fn push_all(from: &Vec<int>, to: &mut Vec<int>) {…} 
! 
fn caller() { 
let mut vec = …; 
push_all(&vec, &mut vec); 
} 
shared reference 
Error: cannot have both shared 
and mutable reference at same 
time 
A &mut T is the only way to access 
the memory it points at
{ 
let mut vec = Vec::new(); 
… 
for i in range(0, vec.len()) { 
let elem: &int = &vec[i]; 
… 
vec.push(…); 
} 
… 
vec.push(…); 
} 
Borrows restrict access to 
the original path for their 
duration. 
Error: vec[i] is borrowed, 
cannot mutate 
OK. loan expired. 
& 
&mut 
no writes, no moves 
no access at all
Abstraction! 
! 
n. freedom from representational qualities in art.
Rust is an extensible language 
• Zero-cost abstraction 
• Rich libraries: 
- Containers 
- Memory management 
- Parallelism 
- … 
• Ownership and borrowing 
let libraries enforce safety.
fn example() { 
let x: Rc<T> = Rc::new(…); 
{ 
let y = x.clone(); 
let z = &*y; 
… 
} // runs destructor for y 
} // runs destructor for x 
x 1 
[0] 
y 
2 1 
… 
z 
Stack Heap
Borrowing and Rc 
fn deref<‘a,T>(r: &’a Rc<T>) -> &’a T { 
… 
} 
Given a borrowed reference 
to an `Rc<T>`… 
…return a reference to the 
`T` inside with same extent 
New reference can be thought of as a kind of sublease. 
Returned reference cannot outlast the original.
fn example() -> &T { 
let x: Rc<T> = Rc::new(…); 
return &*x; 
} // runs destructor for x 
Error: extent of borrow 
exceeds lifetime of `x`
Concurrency! 
! 
n. several computations executing simultaneously, and 
potentially interacting with each other
Data race 
✎ 
✎ 
✎ 
✎ 
Two unsynchronized threads 
accessing same data! 
where at least one writes.
Aliasing 
Mutation 
No ordering 
Data race 
Sound familiar?
Messaging! 
(ownership)
data 
length 
capacity 
fn parent() { 
let (tx, rx) = channel(); 
spawn(proc() {…}); 
let m = rx.recv(); 
} 
proc() { 
let m = Vec::new(); 
… 
tx.send(m); 
} 
rx 
tx 
tx 
m
Shared read-only access! 
(ownership, borrowing)
Arc<Vec<int>> 
(ARC = Atomic Reference Count) 
Owned, so no aliases. Vec<int> 
&Vec<int> 
ref_count 
data 
length 
capacity 
[0] 
[1] 
Shared reference, so 
Vec<int> is immutable.
✎ 
✎ 
Locked mutable access! 
(ownership, borrowing)
fn sync_inc(mutex: &Mutex<int>) { 
let mut data = mutex.lock(); 
*data += 1; 
} 
Destructor releases lock 
Yields a mutable reference to data 
Destructor runs here
And beyond… 
Parallelism is an area of active development. 
! 
Either already have or have plans for: 
- Atomic primitives 
- Non-blocking queues 
- Concurrent hashtables 
- Lightweight thread pools 
- Futures 
- CILK-style fork-join concurrency 
- etc. 
Always data-race free
Parallel! 
! 
adj. occurring or existing at the same time
Concurrent vs parallel 
Blocks 
Concurrent threads Parallel jobs
fn qsort(vec: &mut [int]) { 
let pivot = vec[random(vec.len())]; 
let mid = vec.partition(vec, pivot); 
let (less, greater) = vec.mut_split_at(mid); 
qsort(less); 
qsort(greater); 
} 
let vec: &mut [int] = …; 
[0] [1] [2] [3] […] [n] 
less greater
fn parallel_qsort(vec: &mut [int]) { 
let pivot = vec[random(vec.len())]; 
let mid = vec.partition(vec, pivot); 
let (less, greater) = vec.mut_split(mid); 
parallel::do(&[ 
|| parallel_qsort(less), 
|| parallel_qsort(greater) 
]); 
} 
let vec: &mut [int] = …; 
[0] [1] [2] [3] […] [n] 
less greater
Unsafe! 
! 
adj. not safe; hazardous
Safe abstractions 
fn something_safe(…) { 
! 
unsafe { 
! 
… 
! 
} 
! 
} 
Validates input, etc. 
Trust me. 
• Uninitialized memory 
• Interfacing with C code 
• Building parallel abstractions like ARC 
• …
Status of Rust 
“Rapidly stabilizing.”! 
! 
! 
Goal for 1.0: 
- Stable syntax, core type system 
- Minimal set of core libraries
Conclusions 
• Rust gives control without compromising safety: 
• Zero-cost abstractions 
• Zero-cost safety 
• Guarantees beyond dangling pointers: 
• Iterator invalidation in a broader sense 
• Data race freedom

Guaranteeing Memory Safety in Rust

  • 1.
    Rust Nicholas Matsakis Mozilla Research
  • 2.
    What makes Rustdifferent? C++ Haskell Java ML More Control More Safety Rust: Control and safety
  • 3.
    Why Mozilla? Browsersneed control. Browsers need safety. Servo: Next-generation browser built in Rust.
  • 4.
    What is control? void example() { vector<string> vector; … auto& elem = vector[0]; … } Stack and inline layout. Lightweight references Deterministic destruction [0] string el…em vector data length capacity […] [n] … ‘H’ ‘e’ … Stack Heap C++
  • 5.
    Zero-cost abstraction Abilityto define abstractions that optimize away to nothing. vector data length cap. [0] […] data cap. ‘H’ ‘e’ […] Not just memory layout: - Static dispatch - Template expansion - … Java
  • 6.
    What is safety? void example() { vector<string> vector; … auto& elem = vector[0]; vector.push_back(some_string); cout << elem; } vector data length capacity [0] … [0] [1] elem Aliasing: more than one pointer to same memory. Dangling pointer: pointer to freed memory. C++ Mutating the vector freed old contents.
  • 7.
    What about GC? No control. Requires a runtime. Insufficient to prevent related problems: iterator invalidation, data races.
  • 8.
    void foo(Foo &&f,…) { vec.push_back(f); } C++ Gut it: void foo(const Foo &f, …) { use(f.bar); } Read it: void foo(Foo &f, …) { f.bar = …; } Write it: void foo(unique_ptr<Foo> f, …) { … } Keep it:
  • 9.
    Definitely progress. ! But definitely not safe: ! - Iterator invalidation. - Double moves. - Pointers into stack or freed memory. - Data races. - … all that stuff I talked about before … ! Ultimately, C++ mechanisms are unenforced.
  • 10.
    The Rust Solution Codify and enforce safe patterns using the type system: ! 1. Always have a clear owner. 2. While iterating over a vector, don’t change it. 3. … ! No runtime required.
  • 11.
    Credit where itis due Rust borrows liberally from other great languages: C++, Haskell, ML/Ocaml, Cyclone, Erlang, Java, C#, … Rust has an active, amazing community. ❤
  • 12.
  • 13.
    Observation Danger arisesfrom… Aliasing Mutation Hides dependencies. Causes memory to be freed. { auto& e = v[0]; … v.push_back(…); }
  • 14.
    Three basic patterns fn foo(v: T) { … } Ownership fn foo(v: &T) { … } Shared borrow fn foo(v: &mut T) { … } Mutable borrow
  • 15.
    Ownership! ! n.The act, state, or right of possessing something.
  • 16.
  • 17.
    vec data length capacity 1 2 fn give() { let mut vec = Vec::new(); vec.push(1); vec.push(2); take(vec); … } fn take(vec: Vec<int>) { // … } ! Take ownership ! of a Vec<int> !
  • 18.
    Compiler enforces moves fn give() { let mut vec = Vec::new(); vec.push(1); vec.push(2); take(vec); vec.… push(2); } fn take(vec: Vec<int>) { // … } ! ! Error: ve! c has been moved Prevents: - use after free - double moves - …
  • 19.
    Borrow! ! v.To receive something with the promise of returning it.
  • 20.
  • 21.
  • 22.
    fn lender() { let mut vec = Vec::new(); vec.push(1); vec.push(2); use(&vec); … } fn use(vec: &Vec<int>) { // … } ! ! ! 1 data length capacity vec 2 vec “Shared reference to Vec<int>” Loan out vec
  • 23.
    fn dot_product(vec1: &Vec<int>,vec2: &Vec<int>) elem1 elem2 sum -> int { let mut sum = 0; for (elem1, elem2) in vec1.iter().zip(vec2.iter()) { sum += (*elem1) * (*elem2); } return sum; } walk down matching indices elem1, elem2 are references into the vector 1 2 3 4 5 6 * vec1 vec2
  • 24.
    Why “shared” reference? fn dot_product(vec1: &Vec<int>, vec2: &Vec<int>) -> int {…} ! fn magnitude(vec: &Vec<int>) -> int { sqrt(dot_product(vec, vec)) } two shared references to the same vector — OK!
  • 25.
    Aliasing Mutation Sharedreferences are immutable: fn use(vec: &Vec<int>) { vec.push(3); vec[1] += 2; } * Error: cannot mutate shared reference * Actually: mutation only in controlled circumstances
  • 26.
    Mutable references fnpush_all(from: &Vec<int>, to: &mut Vec<int>) { for elem in from.iter() { to.push(*elem); } } mutable reference to Vec<int> push() is legal
  • 27.
    Mutable references fnpush_all(from: &Vec<int>, to: &mut Vec<int>) { for elem in from.iter() { 1 2 3 to.push(*elem); } } from to elem 1 2 3 …
  • 28.
    What if fromand to are equal? fn push_all(from: &Vec<int>, to: &mut Vec<int>) { for elem in from.iter() { 1 2 3 from to elem 1 2 3 … 1 to.push(*elem); } } dangling pointer
  • 29.
    fn push_all(from: &Vec<int>,to: &mut Vec<int>) {…} ! fn caller() { let mut vec = …; push_all(&vec, &mut vec); } shared reference Error: cannot have both shared and mutable reference at same time A &mut T is the only way to access the memory it points at
  • 30.
    { let mutvec = Vec::new(); … for i in range(0, vec.len()) { let elem: &int = &vec[i]; … vec.push(…); } … vec.push(…); } Borrows restrict access to the original path for their duration. Error: vec[i] is borrowed, cannot mutate OK. loan expired. & &mut no writes, no moves no access at all
  • 31.
    Abstraction! ! n.freedom from representational qualities in art.
  • 32.
    Rust is anextensible language • Zero-cost abstraction • Rich libraries: - Containers - Memory management - Parallelism - … • Ownership and borrowing let libraries enforce safety.
  • 33.
    fn example() { let x: Rc<T> = Rc::new(…); { let y = x.clone(); let z = &*y; … } // runs destructor for y } // runs destructor for x x 1 [0] y 2 1 … z Stack Heap
  • 34.
    Borrowing and Rc fn deref<‘a,T>(r: &’a Rc<T>) -> &’a T { … } Given a borrowed reference to an `Rc<T>`… …return a reference to the `T` inside with same extent New reference can be thought of as a kind of sublease. Returned reference cannot outlast the original.
  • 35.
    fn example() ->&T { let x: Rc<T> = Rc::new(…); return &*x; } // runs destructor for x Error: extent of borrow exceeds lifetime of `x`
  • 36.
    Concurrency! ! n.several computations executing simultaneously, and potentially interacting with each other
  • 37.
    Data race ✎ ✎ ✎ ✎ Two unsynchronized threads accessing same data! where at least one writes.
  • 38.
    Aliasing Mutation Noordering Data race Sound familiar?
  • 39.
  • 40.
    data length capacity fn parent() { let (tx, rx) = channel(); spawn(proc() {…}); let m = rx.recv(); } proc() { let m = Vec::new(); … tx.send(m); } rx tx tx m
  • 41.
    Shared read-only access! (ownership, borrowing)
  • 42.
    Arc<Vec<int>> (ARC =Atomic Reference Count) Owned, so no aliases. Vec<int> &Vec<int> ref_count data length capacity [0] [1] Shared reference, so Vec<int> is immutable.
  • 43.
    ✎ ✎ Lockedmutable access! (ownership, borrowing)
  • 44.
    fn sync_inc(mutex: &Mutex<int>){ let mut data = mutex.lock(); *data += 1; } Destructor releases lock Yields a mutable reference to data Destructor runs here
  • 45.
    And beyond… Parallelismis an area of active development. ! Either already have or have plans for: - Atomic primitives - Non-blocking queues - Concurrent hashtables - Lightweight thread pools - Futures - CILK-style fork-join concurrency - etc. Always data-race free
  • 46.
    Parallel! ! adj.occurring or existing at the same time
  • 47.
    Concurrent vs parallel Blocks Concurrent threads Parallel jobs
  • 48.
    fn qsort(vec: &mut[int]) { let pivot = vec[random(vec.len())]; let mid = vec.partition(vec, pivot); let (less, greater) = vec.mut_split_at(mid); qsort(less); qsort(greater); } let vec: &mut [int] = …; [0] [1] [2] [3] […] [n] less greater
  • 49.
    fn parallel_qsort(vec: &mut[int]) { let pivot = vec[random(vec.len())]; let mid = vec.partition(vec, pivot); let (less, greater) = vec.mut_split(mid); parallel::do(&[ || parallel_qsort(less), || parallel_qsort(greater) ]); } let vec: &mut [int] = …; [0] [1] [2] [3] […] [n] less greater
  • 50.
    Unsafe! ! adj.not safe; hazardous
  • 51.
    Safe abstractions fnsomething_safe(…) { ! unsafe { ! … ! } ! } Validates input, etc. Trust me. • Uninitialized memory • Interfacing with C code • Building parallel abstractions like ARC • …
  • 52.
    Status of Rust “Rapidly stabilizing.”! ! ! Goal for 1.0: - Stable syntax, core type system - Minimal set of core libraries
  • 53.
    Conclusions • Rustgives control without compromising safety: • Zero-cost abstractions • Zero-cost safety • Guarantees beyond dangling pointers: • Iterator invalidation in a broader sense • Data race freedom