[pdf]

Nicholas Matsakis!
Mozilla Research
!
l
ll e
a
r
a
P
Systems programming without the hassle
crashes!
heisenbugs!
fear
2
C/C++: efficiency first!
destructors
memory layout
smart pointers
monomorphization
Research community!
ML/Haskell: safety first!
affine/ownership types
region-based borrowing
ADTs and pattern matching
type inference
no null pointers
closures
traits (typeclasses)
3
High-level coding
// sums all the positive values in `v`
fn sum_pos(v: &[i32]) -> i32 {
let mut sum = 0;
for i in v.iter().filter(|i| **i > 0) {
sum += *i;
}
sum
Iterators.
}
Closures.
4
Assembly code
leaq
(%rdi,%rsi,4), %rcx
xorl
%eax, %eax
jmp .LBB5_1
.LBB5_3:
addl
%edx, %eax
.align 16, 0x90
.LBB5_1:
cmpq
%rdi, %rcx
je .LBB5_4
movl
(%rdi), %edx
addq
$4, %rdi
testl
%edx, %edx
jle .LBB5_1
jmp .LBB5_3
.LBB5_4:
retq
5
Higher-level coding
fn foo(v: &[i32]) -> i32 {
v.iter()
.filter(|i| **i > 0)
.map(|i| *i)
.sum()
}
…generates the same assembly code.
6
Safe
fn this_wont_compile(v: &mut Vec<i32>) -> i32 {
let mut sum = 0;
for &i in v.iter() {
sum += i;
Might
free
if i > 0 { v.push(0); }
underlying buffer.
}
sum
}
error: cannot borrow `*v` as mutable because it is also borrowed
as immutable
if i > 0 { v.push(0); }
^
note: previous borrow of `*v` occurs here; the immutable borrow
prevents subsequent moves or mutable borrows of `*v` until
the borrow ends
for &i in v.iter() {
^
7
Parallel
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)
);
}
Sort left and right
in parallel.
8
Parallel… and safe
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),
Data race.
|| parallel_qsort(less)
);
}
error: closure requires unique access to `less`
but it is already borrowed
|| parallel_qsort(less)
^~~~~~~~~~~~~~~~~~~~~~~
9
Multiparadigm
functional
message-passing
imperative
mutable shared memory
10
Open and welcoming
Rust has been open source from the beginning.
!
Open governance model based on public RFCs.
!
We have an active, amazing community.
❤
11
Whither Safety?
https://www.flickr.com/photos/langtind/2217639550/in/photostream/
12
No, it’s a
double free!
It’s a
dangling
pointer!
No, it’s a
data race!
Simultaneous
Aliasing and
Mutation
No,
it’s iterator
invalidation!
13
The Big Idea
Ownership and borrowing:!
!
1. All memory has a clear owner.
2. Others can borrow from the owner.
3. Owner cannot free or mutate the
memory while it is borrowed.
14
fn main() {
fn sum(v: Vec<i32>) -> i32 {
let mut v = vec![…];
let mut s = 0;
let sum = sum(v);
for i in 0..v.len() {
println!(“{:?}”, sum);
s += v[i];
…
}
}
s
}
Give the vector
to sum()
Take ownership
of a Vec<i32>
Ownership
15
Compiler enforces moves
fn main() {
fn sum(v: Vec<i32>) -> i32 {
let mut v = vec![…];
let mut s = 0;
let sum = sum(v);
for i in 0..v.len() {
println!(“{:?}”, sum);
s += v[i];
v[0]
+= 1;
…
}
}
s
}
error: use of moved value: `v`
v[0] += 1;
^
note: `v` moved here because it has type `Vec<i32>`,
which is non-copyable
let sum = sum(v);
^
16
Two birds, one stone
Ownership solves:
- Memory management
- Message passing
- Strong encapsulation
17
fn main() {
fn sum(v: &Vec<i32>) -> i32 {
let mut v = vec![…];
let mut s = 0;
let sum = sum(&v);
for i in 0..v.len() {
println!(“{:?}”, sum);
s += v[i];
v[0] += 1;
}
}
s
}
Lend the vector
Take a reference
to a Vec<i32>
Shared borrow
18
fn main() {
let mut v = vec![…];
sum(&mut v);
v[0] += 1;
}
Lend the vector
mutably
fn sum(v: &mut Vec<i32>) {
let mut s = 0;
for i in 0..v.len() {
s += v[i];
v[i] = s;
}
}
Take a mutable reference
to a Vec<i32>
Mutable borrow
19
fn example() {
let mut names = Vec::new();
names.push(..);
names.push(..);
let name = &names[1];
names.push(..);
print(name);
}
Mutating the vector
freed old contents.
“brson”
“pcwalton”
“acrichto”
names
data
“brson”
length
“pcwalton”
capacity
name
Sharing: more than
pointerpointer
to same
Danglingone
pointer:
memory.
to freed memory.
20
Rust solution
Compile-time read-write-lock:!
!
Creating a shared reference to X “read locks” X.
- Other readers OK.
- No writers.
- Lock lasts until reference goes out of scope.
!
Creating a mutable reference to X “writes locks” X.
- No other readers or writers.
- Lock lasts until reference goes out of scope.
Never have a reader/writer at same time.
21
fn example() {
let mut names = Vec::new();
names.push(“brson”);
names.push(“pcwalton”);
let name = &names[1];
names.push(“acrichto”);
println!(“{:?}”, name);
}
Error: cannot mutate
`names` while borrowed
Borrow “locks”
`names` until `name`
goes out of scope
http://is.gd/jeKW1E
22
Back to qsort
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(
Creating closure
|| parallel_qsort(less),
borrows `less`
|| parallel_qsort(less)
mutably
);
}
This closure
also needs a
mutable borrow
23
Hack without fear!
24