Nicholas Matsakis!
Mozilla Research
1
So you want to start a project
Bugs!
C++?
Control!
Dangling pointers Double frees
Segmentation faults Data races
…
2
What about GC?
No control.
Requires a runtime.
Insufficient to prevent related problems:
resource management, data races.
3
Rust’s Solution
Type system enforces 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.
4
Ownership/Borrowing
Memory
safety
Data-race
freedom
No need for
a runtime
GCC++
5
Credit where it is due
Rust has an active, amazing community.
❤
6
Ownership!
!
n. The act, state, or right of possessing something.
7
Ownership
8
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<i32>) {
// …
}
!
!
!
Take ownership
of a Vec<i32>
9
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<i32>) {
// …
}
!
!
!Error: vec has been moved
Prevents:
- use after free
- double moves
- …
10
Borrow!
!
v. To receive something with the promise of returning it.
11
* Actually: mutation only in controlled circumstances
*
Shared borrow (&T)
Sharing Mutation
Mutable borrow (&mut T)
13
Sharing Mutation
fn example() {
let mut names = Vec::new();
names.push(..);
names.push(..);
let name = &names[1];
names.push(..);
print(name);
}
names
data
length
capacity
“brson”
“pcwalton”
name
“brson”
“pcwalton”
“acrichto”
Sharing: more than
one pointer to same
memory.
Dangling pointer: pointer
to freed memory.
Mutating the vector
freed old contents.
14
fn example() {
let mut names = Vec::new();
names.push(..);
names.push(..);
let name = &names[1];
names.push(..);
print(name);
}
Borrow “locks”
`names` until `name`
goes out of scopeError: cannot mutate
`names` while borrowed
15
fn example() {
let mut names = Vec::new();
names.push(..);
names.push(..);
while something-or-other {
let name = &names[1];
…
names.push(..);
print(name);
}
names.push(..);
}
Outside of borrow scope — OK.
Scope of borrow
in this case covers
only the loop body.
16
Parallel!
!
adj. occurring or existing at the same time
17
Data race
Two unsynchronized threads
accessing same data!
where at least one writes.
✎
✎
18
Sharing
Mutation
No ordering
Data race
Sound familiar?
19
Double buffering
A B
A B
20
fn process(a: &mut Vec<i32>, b: &mut Vec<i32>) {
while !done {
computation(a, b);
computation(b, a);
}
}
!
fn computation(input: &Vec<i32>,
output: &mut Vec<i32>) {
…
}
Double buffering
Temporarily
immutable
21
Double buffering in parallel
A B
A B
B
A
22
fn par_computation(input: &Vec<i32>,
output: &mut Vec<i32>) {
let len = output.len();
let (l, r) = output.split_at_mut(len/2);
let left_thread = scoped(|| computation(input, l));
computation(input, r);
left_thread.join();
}
Create two disjoint views
Start a “scoped” thread
to process `l`Process `r` in the
main thread
Block until `left_thread`
completes (implicit)
23
What about data races?
fn par_computation(input: &Vec<i32>,
output: &mut Vec<i32>) {
let len = output.len();
let (l, r) = output.split_at_mut(len/2);
let left_thread = scoped(|| computation(input, l));
computation(input, l);
left_thread.join();
}
Process `l` in
both threads?
24
fn par_computation(input: &Vec<i32>,
output: &mut Vec<i32>) {
let len = output.len();
let (l, r) = output.split_at_mut(len/2);
let left_thread = scoped(|| computation(input, l));
computation(input, l);
left_thread.join();
}
Shared borrow of
`input` — OK
Shared borrow of `input`
Mutable borrow of `l`
Error: `l` is mutably
borrowed, cannot access
25
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.
All “just libraries”. And all ensure data-race freedom.
26
Unsafe!
!
adj. not safe; hazardous
27
Safe abstractions
unsafe {
…
}
• Useful for:
• Uninitialized memory
• Interfacing with C code
• Building parallel abstractions
• Ownership/borrowing permit creating
safe abstraction boundaries.
Trust me.
fn something_safe(…) {
!
!
!
!
}
Validates input, etc.
28
Conclusions
• Rust combines high-level features with low-level
control.
• Rust gives stronger safety guarantees than GC:
• No null pointers
• Data race freedom
29

Intro to Rust from Applicative / NY Meetup

  • 1.
  • 2.
    So you wantto start a project Bugs! C++? Control! Dangling pointers Double frees Segmentation faults Data races … 2
  • 3.
    What about GC? Nocontrol. Requires a runtime. Insufficient to prevent related problems: resource management, data races. 3
  • 4.
    Rust’s Solution Type systemenforces 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. 4
  • 5.
  • 6.
    Credit where itis due Rust has an active, amazing community. ❤ 6
  • 7.
    Ownership! ! n. The act,state, or right of possessing something. 7
  • 8.
  • 9.
    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<i32>) { // … } ! ! ! Take ownership of a Vec<i32> 9
  • 10.
    fn give() { letmut vec = Vec::new(); vec.push(1); vec.push(2); take(vec); … } vec.push(2); Compiler enforces moves fn take(vec: Vec<i32>) { // … } ! ! !Error: vec has been moved Prevents: - use after free - double moves - … 10
  • 11.
    Borrow! ! v. To receivesomething with the promise of returning it. 11
  • 12.
    * Actually: mutationonly in controlled circumstances * Shared borrow (&T) Sharing Mutation
  • 13.
    Mutable borrow (&mutT) 13 Sharing Mutation
  • 14.
    fn example() { letmut names = Vec::new(); names.push(..); names.push(..); let name = &names[1]; names.push(..); print(name); } names data length capacity “brson” “pcwalton” name “brson” “pcwalton” “acrichto” Sharing: more than one pointer to same memory. Dangling pointer: pointer to freed memory. Mutating the vector freed old contents. 14
  • 15.
    fn example() { letmut names = Vec::new(); names.push(..); names.push(..); let name = &names[1]; names.push(..); print(name); } Borrow “locks” `names` until `name` goes out of scopeError: cannot mutate `names` while borrowed 15
  • 16.
    fn example() { letmut names = Vec::new(); names.push(..); names.push(..); while something-or-other { let name = &names[1]; … names.push(..); print(name); } names.push(..); } Outside of borrow scope — OK. Scope of borrow in this case covers only the loop body. 16
  • 17.
    Parallel! ! adj. occurring orexisting at the same time 17
  • 18.
    Data race Two unsynchronizedthreads accessing same data! where at least one writes. ✎ ✎ 18
  • 19.
  • 20.
  • 21.
    fn process(a: &mutVec<i32>, b: &mut Vec<i32>) { while !done { computation(a, b); computation(b, a); } } ! fn computation(input: &Vec<i32>, output: &mut Vec<i32>) { … } Double buffering Temporarily immutable 21
  • 22.
    Double buffering inparallel A B A B B A 22
  • 23.
    fn par_computation(input: &Vec<i32>, output:&mut Vec<i32>) { let len = output.len(); let (l, r) = output.split_at_mut(len/2); let left_thread = scoped(|| computation(input, l)); computation(input, r); left_thread.join(); } Create two disjoint views Start a “scoped” thread to process `l`Process `r` in the main thread Block until `left_thread` completes (implicit) 23
  • 24.
    What about dataraces? fn par_computation(input: &Vec<i32>, output: &mut Vec<i32>) { let len = output.len(); let (l, r) = output.split_at_mut(len/2); let left_thread = scoped(|| computation(input, l)); computation(input, l); left_thread.join(); } Process `l` in both threads? 24
  • 25.
    fn par_computation(input: &Vec<i32>, output:&mut Vec<i32>) { let len = output.len(); let (l, r) = output.split_at_mut(len/2); let left_thread = scoped(|| computation(input, l)); computation(input, l); left_thread.join(); } Shared borrow of `input` — OK Shared borrow of `input` Mutable borrow of `l` Error: `l` is mutably borrowed, cannot access 25
  • 26.
    And beyond… Parallelism 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. All “just libraries”. And all ensure data-race freedom. 26
  • 27.
  • 28.
    Safe abstractions unsafe { … } •Useful for: • Uninitialized memory • Interfacing with C code • Building parallel abstractions • Ownership/borrowing permit creating safe abstraction boundaries. Trust me. fn something_safe(…) { ! ! ! ! } Validates input, etc. 28
  • 29.
    Conclusions • Rust combineshigh-level features with low-level control. • Rust gives stronger safety guarantees than GC: • No null pointers • Data race freedom 29