KEMBAR78
Rust: Unlocking Systems Programming | PDF
Aaron Turon	

Mozilla Research
InfoQ.com: News & Community Site
• 750,000 unique visitors/month
• Published in 4 languages (English, Chinese, Japanese and Brazilian
Portuguese)
• Post content from our QCon conferences
• News 15-20 / week
• Articles 3-4 / week
• Presentations (videos) 12-15 / week
• Interviews 2-3 / week
• Books 1 / month
Watch the video with slide
synchronization on InfoQ.com!
http://www.infoq.com/presentations
/rust-thread-safety
Purpose of QCon
- to empower software development by facilitating the spread of
knowledge and innovation
Strategy
- practitioner-driven conference designed for YOU: influencers of
change and innovation in your teams
- speakers and topics driving the evolution and innovation
- connecting and catalyzing the influencers and innovators
Highlights
- attended by more than 12,000 delegates since 2007
- held in 9 cities worldwide
Presented at QCon San Francisco
www.qconsf.com
Rust is a systems programming language
that runs blazingly fast, prevents nearly all
segfaults, and guarantees thread safety. 	

- https://www.rust-lang.org/
Safety
Control
C C++
Go
Java
Haskell
Scala
“Low-level”
Conventional wisdom
The Essence of Rust
Low-level == Unsafe
+ Safe
Why Rust?
- You’re already doing systems programming, 

want safety or expressiveness.	

!
- You wish you could do some systems work	

- Maybe as an embedded piece in your

Java, Python, JS, Ruby, …
Why Mozilla?
Browsers need control.
Browsers need safety.
Servo: Next-generation
browser built in Rust.
Rust: New language for 	

safe systems programming.
What is control?
void example() {
vector<string> vector;
…
auto& elem = vector[0];
…
}
string[0]
…elem
vector
data
length
capacity
[0]
[n]
[…]
…
‘H’
…
‘e’
Stack and inline layout.
Interior references
Deterministic destruction
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, many others.
Ownership & Borrowing
Memory	

safety
Data-race	

freedom	

(and more)
No need for	

a runtime
GCC++
… Plus lots of goodies
- Pattern matching	

- Traits	

- “Smart” pointers	

- Metaprogramming	

- Package management (think Bundler)
TL;DR: Rust is a modern language
Ownership
!
n. The act, state, or right of possessing something.
Ownership (T)
Aliasing Mutation
vec
data
length
capacity
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 aVec<int>
fn give() {
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
take(vec);
…
}
vec.push(2);
Compiler enforces moves
fn take(vec: Vec<int>) {
// …
}
!
!
!Error: vec has been moved
Prevents:	

- use after free	

- double moves	

- …
Borrow
!
v. To receive something with the promise of returning it.
Shared borrow (&T)
Aliasing Mutation
Mutable borrow (&mut T)
Aliasing Mutation
fn lender() {
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
use(&vec);
…
}
fn use(vec: &Vec<int>) {
// …
}
!
!
!
1
2vec
data
length
capacity
vec
“Shared reference	

toVec<int>”
Loan out vec
fn use(vec: &Vec<int>) {
vec.push(3);
vec[1] += 2;
}
Shared references are immutable:
Error: cannot mutate shared reference
* Actually: mutation only in controlled circumstances
*
Aliasing Mutation
fn push_all(from: &Vec<int>, to: &mut Vec<int>) {
for elem in from {
to.push(*elem);
}
}
Mutable references
mutable reference to Vec<int>
push() is legal
1
2
3
from
to
elem
1
…
fn push_all(from: &Vec<int>, to: &mut Vec<int>) {
for elem in from {
to.push(*elem);
}
}
Mutable references
What if from and to are equal?
1
2
3
from
to
elem
1
2
3
…
1
fn push_all(from: &Vec<int>, to: &mut Vec<int>) {
for elem in from {
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 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
Concurrency
!
n. several computations executing simultaneously, and
potentially interacting with each other.
Rust’s vision for concurrency
Originally:	

!
Now:
only isolated message passing
libraries for many paradigms,	

using ownership to avoid footguns,	

guaranteeing no data races
Data race
Two unsynchronized threads	

accessing same data
where at least one writes.
✎
✎
Aliasing
Mutation
No ordering
Data race
Sound familiar?
No data races =	

No accidentally-shared state.	

!
All sharing is explicit!
*some_value = 5;
return *some_value == 5; // ALWAYS true
Messaging
(ownership)
data
length
capacity
data
length
capacity
move || {
let m = Vec::new();
…
tx.send(m);
}
rx
tx
tx
m
fn parent() {
let (tx, rx) = channel();
spawn(move || {…});
let m = rx.recv();
}
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, releasing lock
Disjoint, scoped access
(borrowing)
✎
✎
fn qsort(vec: &mut [int]) {
if vec.len() <= 1 { return; }
let pivot = vec[random(vec.len())];
let mid = vec.partition(vec, pivot);
let (less, greater) = vec.split_at_mut(mid);
qsort(less);
qsort(greater);
}
[0] [1] [2] [3] […] [n]
let vec: &mut [int] = …;
less greater
fn split_at_mut(&mut self, mid: usize)
-> (&mut [T], & mut [T])
[0] [1] [2] [3] […] [n]
let vec: &mut [int] = …;
less greater
fn parallel_qsort(vec: &mut [int]) {
if vec.len() <= 1 { return; }
let pivot = vec[random(vec.len())];
let mid = vec.partition(vec, pivot);
let (less, greater) = vec.split_at_mut(mid);
parallel::join(
|| parallel_qsort(less),
|| parallel_qsort(greater)
);
}
Arc<Vec<int>>: Send
Rc<Vec<int>> : !Send
fn send<T: Send>(&self, t: T)
Only “sendable” types
Static checking for thread safety
And beyond…
Concurrency 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
Unsafe
!
adj. not safe; hazardous
Safe abstractions
unsafe {
…
}
Useful for:	

	

 Bending mutation/aliasing rules (split_at_mut)	

	

 Interfacing with C code
Trust me.
fn something_safe(…) {
!
!
!
!
}
Validates input, etc.
Ownership enables safe abstraction boundaries.
Community
!
n. A feeling of fellowship with others sharing similar goals.
“The Rust community seems to be
populated entirely by human beings.
I have no idea how this was done.”	

— Jamie Brandon
It takes a village…
Community focus from the start:	

	

 Rust 1.0 had > 1,000 contributors	

	

 Welcoming, pragmatic culture	

!
Developed “in the open”	

	

 Much iteration;	

humility is key!	

!
Clear leadership	

	

 Mix of academic and engineering backgrounds	

	

 “Keepers of the vision”
Memory safety	

Concurrency	

Abstraction	

Stability
without
garbage collection	

data races	

overhead	

stagnation
Hack without fear!
Articulating the vision
Watch the video with slide synchronization on
InfoQ.com!
http://www.infoq.com/presentations/rust-
thread-safety

Rust: Unlocking Systems Programming

  • 1.
  • 2.
    InfoQ.com: News &Community Site • 750,000 unique visitors/month • Published in 4 languages (English, Chinese, Japanese and Brazilian Portuguese) • Post content from our QCon conferences • News 15-20 / week • Articles 3-4 / week • Presentations (videos) 12-15 / week • Interviews 2-3 / week • Books 1 / month Watch the video with slide synchronization on InfoQ.com! http://www.infoq.com/presentations /rust-thread-safety
  • 3.
    Purpose of QCon -to empower software development by facilitating the spread of knowledge and innovation Strategy - practitioner-driven conference designed for YOU: influencers of change and innovation in your teams - speakers and topics driving the evolution and innovation - connecting and catalyzing the influencers and innovators Highlights - attended by more than 12,000 delegates since 2007 - held in 9 cities worldwide Presented at QCon San Francisco www.qconsf.com
  • 4.
    Rust is asystems programming language that runs blazingly fast, prevents nearly all segfaults, and guarantees thread safety. - https://www.rust-lang.org/
  • 5.
  • 6.
    Conventional wisdom The Essenceof Rust Low-level == Unsafe + Safe
  • 7.
    Why Rust? - You’realready doing systems programming, 
 want safety or expressiveness. ! - You wish you could do some systems work - Maybe as an embedded piece in your
 Java, Python, JS, Ruby, …
  • 8.
    Why Mozilla? Browsers needcontrol. Browsers need safety. Servo: Next-generation browser built in Rust. Rust: New language for safe systems programming.
  • 9.
    What is control? voidexample() { vector<string> vector; … auto& elem = vector[0]; … } string[0] …elem vector data length capacity [0] [n] […] … ‘H’ … ‘e’ Stack and inline layout. Interior references Deterministic destruction Stack Heap C++
  • 10.
    Zero-cost abstraction Ability todefine abstractions that optimize away to nothing. vector data length cap. [0] […] data cap. ‘H’ ‘e’ […] Not just memory layout: - Static dispatch - Template expansion - … Java
  • 11.
    What is safety? voidexample() { 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.
  • 12.
    What about GC? Nocontrol. Requires a runtime. Insufficient to prevent related problems: iterator invalidation, data races, many others.
  • 13.
  • 14.
    … Plus lotsof goodies - Pattern matching - Traits - “Smart” pointers - Metaprogramming - Package management (think Bundler) TL;DR: Rust is a modern language
  • 15.
    Ownership ! n. The act,state, or right of possessing something.
  • 16.
  • 17.
    vec data length capacity vec data length capacity 1 2 fn give() { letmut vec = Vec::new(); vec.push(1); vec.push(2); take(vec); … } fn take(vec: Vec<int>) { // … } ! ! ! Take ownership of aVec<int>
  • 18.
    fn give() { letmut vec = Vec::new(); vec.push(1); vec.push(2); take(vec); … } vec.push(2); Compiler enforces moves fn take(vec: Vec<int>) { // … } ! ! !Error: vec has been moved Prevents: - use after free - double moves - …
  • 19.
    Borrow ! v. To receivesomething with the promise of returning it.
  • 20.
  • 21.
    Mutable borrow (&mutT) Aliasing Mutation
  • 22.
    fn lender() { letmut vec = Vec::new(); vec.push(1); vec.push(2); use(&vec); … } fn use(vec: &Vec<int>) { // … } ! ! ! 1 2vec data length capacity vec “Shared reference toVec<int>” Loan out vec
  • 23.
    fn use(vec: &Vec<int>){ vec.push(3); vec[1] += 2; } Shared references are immutable: Error: cannot mutate shared reference * Actually: mutation only in controlled circumstances * Aliasing Mutation
  • 24.
    fn push_all(from: &Vec<int>,to: &mut Vec<int>) { for elem in from { to.push(*elem); } } Mutable references mutable reference to Vec<int> push() is legal
  • 25.
    1 2 3 from to elem 1 … fn push_all(from: &Vec<int>,to: &mut Vec<int>) { for elem in from { to.push(*elem); } } Mutable references
  • 26.
    What if fromand to are equal? 1 2 3 from to elem 1 2 3 … 1 fn push_all(from: &Vec<int>, to: &mut Vec<int>) { for elem in from { to.push(*elem); } } dangling pointer
  • 27.
    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
  • 28.
    { let mut vec= Vec::new(); … for i in 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
  • 29.
    Concurrency ! n. several computationsexecuting simultaneously, and potentially interacting with each other.
  • 30.
    Rust’s vision forconcurrency Originally: ! Now: only isolated message passing libraries for many paradigms, using ownership to avoid footguns, guaranteeing no data races
  • 31.
    Data race Two unsynchronizedthreads accessing same data where at least one writes. ✎ ✎
  • 32.
  • 33.
    No data races= No accidentally-shared state. ! All sharing is explicit! *some_value = 5; return *some_value == 5; // ALWAYS true
  • 34.
  • 35.
    data length capacity data length capacity move || { letm = Vec::new(); … tx.send(m); } rx tx tx m fn parent() { let (tx, rx) = channel(); spawn(move || {…}); let m = rx.recv(); }
  • 36.
  • 37.
    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, releasing lock
  • 38.
  • 39.
    fn qsort(vec: &mut[int]) { if vec.len() <= 1 { return; } let pivot = vec[random(vec.len())]; let mid = vec.partition(vec, pivot); let (less, greater) = vec.split_at_mut(mid); qsort(less); qsort(greater); } [0] [1] [2] [3] […] [n] let vec: &mut [int] = …; less greater
  • 40.
    fn split_at_mut(&mut self,mid: usize) -> (&mut [T], & mut [T])
  • 41.
    [0] [1] [2][3] […] [n] let vec: &mut [int] = …; less greater fn parallel_qsort(vec: &mut [int]) { if vec.len() <= 1 { return; } let pivot = vec[random(vec.len())]; let mid = vec.partition(vec, pivot); let (less, greater) = vec.split_at_mut(mid); parallel::join( || parallel_qsort(less), || parallel_qsort(greater) ); }
  • 42.
    Arc<Vec<int>>: Send Rc<Vec<int>> :!Send fn send<T: Send>(&self, t: T) Only “sendable” types Static checking for thread safety
  • 43.
    And beyond… Concurrency isan 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
  • 44.
  • 45.
    Safe abstractions unsafe { … } Usefulfor: Bending mutation/aliasing rules (split_at_mut) Interfacing with C code Trust me. fn something_safe(…) { ! ! ! ! } Validates input, etc. Ownership enables safe abstraction boundaries.
  • 46.
    Community ! n. A feelingof fellowship with others sharing similar goals.
  • 47.
    “The Rust communityseems to be populated entirely by human beings. I have no idea how this was done.” — Jamie Brandon
  • 48.
    It takes avillage… Community focus from the start: Rust 1.0 had > 1,000 contributors Welcoming, pragmatic culture ! Developed “in the open” Much iteration; humility is key! ! Clear leadership Mix of academic and engineering backgrounds “Keepers of the vision”
  • 50.
    Memory safety Concurrency Abstraction Stability without garbage collection dataraces overhead stagnation Hack without fear! Articulating the vision
  • 51.
    Watch the videowith slide synchronization on InfoQ.com! http://www.infoq.com/presentations/rust- thread-safety