intro to rust from applicative / ny meetup
TRANSCRIPT
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
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
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
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
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 threadBlock 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
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