Rust Intro Felix and the Rust Team

(caveat: above is using unstable thread::scoped API)

See also Fearless Concurrency blog post.

// the backing storage for `v`)

(when compiled in "release mode")

// sums all the positive values in `v`

The below loop demo compiles down to tight code:

Rust: What? Why? (How?)

All linked from top of http://www.rust-lang.org/

// (p.s. where is `return`?) // |

At scope end, initialized variables are cleaned up

Once initialized, local owns its data (vector's backing store)

Borrowing

Moves insufficient on their own Imagine programming without reuse #[ test ] fn moves_insufficient() { let vec = expensive_vector_computation(); let result1 = some_vec_calculation(vec); // <-- `vec` moved here let result2 = other_calculation(vec); // oops, `vec` is *gone* combine(result1, result2); } error: use of moved value: `vec` [E0382] let result2 = other_calculation(vec); // oops ^~~ note: `vec` moved here because it has type `collections::vec::Vec<i32>`, which is non-copyable let result1 = some_vec_calculation(vec); // <-- `vec` moved here ^~~

Want: access to owned data without consuming it

Thus, "borrowing" #[ test ] fn moves_insufficient() { let vec = expensive_vector_computation(); let result1 = some_vec_calculation(&vec); // <-- lend out `vec` let result2 = other_calculation(&vec); // <-- lend again, no prob combine(result1, result2); } // (`vec` is destroyed/freed aka "dropped" here) &vec ~~~~ | a borrow expression ("mo' features, mo' problems")

Big Question Why are safety violations generally hard to detect? It is due to aliasing

Borrows reintroduce aliasing Q: How to ensure safety in presence of aliasing? A: Restrict the aliasing

Simple metaphor: RW lock Read-only operations do not require exclusive access

Exclusive access requires there are no other readers Rust uses analogous model (at compile-time) for borrows

Borrowing: Basic Mental Model Base types T e.g. char , Vec < i32 > If type copyable, then you can always copy it You can move it only if no borrow active

Immutable borrows: &T "Read-only." Freely aliasable; copyable (i.e. "many readers")

Mutable borrows: & mut T Read/Write. Exclusive access; non-copy (i.e. "at most one writer")



Immutable borrows

Borrowing (immutably) #[ test ] fn show_some_borrows() { let v1 = vec! [ 1 , 2 , 3 ]; let v2 = vec! [ 4 , 5 , 6 ]; let r1 = &v1; let r2 = &v2; foo(r1); foo(r2); } fn foo< 'a >(v: & 'a Vec < i32 >) { println! ( "v[1]: {}" , v[ 1 ]); } &v1 and &v2 are borrowing v1 and v2 .

Scopes and Lifetimes #[ test ] fn show_some_lifetimes() { let v1 = vec! [ 1 , 2 , 3 ]; // + let v2 = vec! [ 4 , 5 , 6 ]; // + | // | | let r1 = &v1; // + | | let r2 = &v2; // + | | | foo(r1); // | | | | foo(r2); // 'r2 'r1 'v2 'v1 // | | | | } // + + + + fn foo< 'a >(v: & 'a Vec < i32 >) { println! ( "v[1]: {}" , v[ 1 ]); } Each borrow selects "appropriate" lifetime 'a .

Borrow Checking Prevents Errors fn borrow_checking_prevents_errors() { let v1 = vec! [ 1 , 2 , 3 ]; // + // | let r1 = &v1; // + 'v1 // | | consume(v1); // 'r1 (moved) foo(r1); // | } // + fn foo< 'a >(v: & 'a Vec < i32 >) { println! ( "v[1]: {}" , v[ 1 ]); } fn consume(v: Vec < i32 >) { /* `v` *dropped* at scope end */ } foo(r1) attempts an indirect read of v1 error: cannot move out of `v1` because it is borrowed consume(v1); ^~ note: borrow of `v1` occurs here let r1 = &v1; ^~

Lifetimes and Lexical Scope fn borrow_checking_may_seem_simple_minded() { let v1 = vec! [ 1 , 2 , 3 ]; // + // | let r1 = &v1; // + 'v1 // | | consume(v1); // 'r1 (moved) // (no call to read) // | } // + fn foo< 'a >(v: & 'a Vec < i32 >) { println! ( "v[1]: {}" , v[ 1 ]); } fn consume(v: Vec < i32 >) { } error: cannot move out of `v1` because it is borrowed consume(v1); ^~ note: borrow of `v1` occurs here let r1 = &v1; ^~ (artifact of lexical-scope based implementation)

Built on lexical scopes, but non-trivial #[ test ] fn copying_can_extend_a_borrows_lifetime_1() { fn foo< 'a >(v: & 'a Vec < i32 >) { println! ( "v[1]: {}" , v[ 1 ]); } let v1 = vec! [ 1 , 2 , 3 ]; // + let v2 = vec! [ 4 , 5 , 6 ]; // | + let r2 = { // | | let r1 = &v1; // + | | // ~~~ <--- A // | | | foo(r1); // 'r1 | | &v2 // | 'v1 'v2 }; // + + | | // (maybe mutate `v1` // | | | // here someday?) // | | | // 'r2 | | foo(r2); // | | | } // + + + How long should the borrow &v1 last? In this case, the borrow marked "A" only needs to last long enough for the call to foo(r1) ; after that point, the borrow is never needed.

Built on lexical scopes, but non-trivial #[ test ] fn copying_can_extend_a_borrows_lifetime_2() { fn foo< 'a >(v: & 'a Vec < i32 >) { println! ( "v[1]: {}" , v[ 1 ]); } let v1 = vec! [ 1 , 2 , 3 ]; // + let v2 = vec! [ 4 , 5 , 6 ]; // | + let r2 = { // | | let r1 = &v1; // + | | // ~~~ <--- A // | | | foo(r1); // 'r1 | | r1 // <--------- B // | 'v1 'v2 }; // + + | | // (maybe mutate `v1` // | | | // here someday?) // | | | // 'r2 | | foo(r2); // | | | } // + + + How long should the borrow &v1 last now? In this case, the borrow marked "A" needs to last longer! The value of r1 is being copied into r2 on the line marked "B", so the borrow marked "A" needs to include the scope of both 'r1 and 'r2 .

imm-borrows: can be copied freely (super super useful to be able to share readable data!)

imm-borrows: can be copied freely Implications: must assume aliased (perhaps by another thread)

therefore not safe to mutate in general #[ test ] fn demo_cannot_mutate_imm_borrow() { let mut v1 = vec! [ 1 , 2 , 3 ]; let b = &v1; let (b1, b2, b3) = (b, b, b); try_modify(b); println! ( "v1: {:?}" , v1); } fn try_modify(v: & Vec < i32 >) { v.push( 4 ); } error: cannot borrow immutable borrowed content `*v` as mutable v.push(4); ^

imm-borrows: can be copied freely Implications: must assume aliased (perhaps by another thread)

therefore not safe to mutate in general #[ test ] fn demo_cannot_mutate_imm_borrow() { let mut v1 = vec! [ 1 , 2 , 3 ]; let b = &v1; let (b1, b2, b3) = (b, b, b); try_modify(b); println! ( "v1: {:?}" , v1); } fn try_modify(v: & Vec < i32 >) { v.push( 4 ); } WHAT A BUMMER!!!

"... I want my imperative algorthms! ..."

& mut borrows #[ test ] fn demo_can_mutate_mut_borrow() { let mut v1 = vec! [ 1 , 2 , 3 ]; modify(& mut v1); println! ( "v1: {:?}" , v1); } fn modify(v: & mut Vec < i32 >) { v.push( 4 ); } v1: [1, 2, 3, 4]

What does & mut mean (crucial) &mut is not about "being the only way to mutate" It is about exclusive access An operation requiring exclusive access should either: take ownership, or,

take an &mut -reference

& mut is about exclusive access " mut means 'mutate' ..." is a fiction For many types, safe mutation does require exclusive access vec.push( 4 ); // requires `vec: &mut Vec<_>`, for safe manipulation of backing store " mut means 'mutate' ..." is a convenient fiction (For related naming drama, do a search for: "mutpocalypse")

& mut safety enforcement

Data has at most one & mut borrow fn take2< 'a >(v1: & 'a mut Vec < i32 >, v2: & 'a Vec < i32 >) { } #[ test ] fn demo_cannot_mut_borrow_multiple_times() { let mut v1 = vec! [ 1 , 2 , 3 ]; let mut v2 = vec! [ 1 , 2 , 3 ]; take2(& mut v1, & mut v2); // <-- this is okay take2(& mut v1, & mut v1); } error: cannot borrow `v1` as mutable more than once at a time take2(&mut v1, &mut v1); ^~ note: previous borrow of `v1` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `v1` until the borrow ends take2(&mut v1, &mut v1); ^~

Cannot alias & mut -borrowed data fn take2< 'a >(v1: & 'a mut Vec < i32 >, v2: & 'a Vec < i32 >) { } #[ test ] fn demo_cannot_alias_mut_borrowed_data() { let mut v1 = vec! [ 1 , 2 , 3 ]; let mut v2 = vec! [ 1 , 2 , 3 ]; take2(& mut v1, &v2); // <-- this is okay take2(& mut v1, &v1); } error: cannot borrow `v1` as immutable because it is also borrowed as mutable take2(&mut v1, &v1); ^~ note: previous borrow of `v1` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `v1` until the borrow ends take2(&mut v1, &v1); ^~

& mut T is non-copy fn take2< 'a >(v1: & 'a mut Vec < i32 >, v2: & 'a Vec < i32 >) { } #[ test ] fn demo_cannot_copy_mut_borrows() { let mut v1 = vec! [ 1 , 2 , 3 ]; let b = & mut v1; let c = b; take2(b, c); } error: use of moved value: `*b` [E0382] take2(b, c); ^ note: `b` moved here because it has type `&mut collections::vec::Vec<i32>`, which is moved by default let c = b; ^ (ensures exclusive access)