Guaranteeing Memory Safety in Rust
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Guaranteeing Memory Safety in Rust

  • 133 views
Uploaded on

An overview of how Rust guarantees memory safety.

An overview of how Rust guarantees memory safety.

More in: Software
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
133
On Slideshare
127
From Embeds
6
Number of Embeds
2

Actions

Shares
Downloads
8
Comments
0
Likes
1

Embeds 6

http://design.inf.usi.ch 5
http://design.inf.unisi.ch 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Rust Nicholas Matsakis Mozilla Research
  • 2. What makes Rust different? C++ Haskell Java ML More Control More Safety Rust: Control and safety
  • 3. Why Mozilla? Browsers need control. Browsers need safety. Servo: Next-generation browser built in Rust.
  • 4. What is control? void example() { vector<string> vector; … auto& elem = vector[0]; … } Stack and inline layout. Lightweight references Deterministic destruction [0] string el…em vector data length capacity […] [n] … ‘H’ ‘e’ … Stack Heap C++
  • 5. 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
  • 6. 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.
  • 7. What about GC? No control. Requires a runtime. Insufficient to prevent related problems: iterator invalidation, data races.
  • 8. void foo(Foo &&f, …) { vec.push_back(f); } C++ Gut it: void foo(const Foo &f, …) { use(f.bar); } Read it: void foo(Foo &f, …) { f.bar = …; } Write it: void foo(unique_ptr<Foo> f, …) { … } Keep it:
  • 9. Definitely progress. ! But definitely not safe: ! - Iterator invalidation. - Double moves. - Pointers into stack or freed memory. - Data races. - … all that stuff I talked about before … ! Ultimately, C++ mechanisms are unenforced.
  • 10. The Rust Solution Codify and enforce safe patterns using the type system: ! 1. Always have a clear owner. 2. While iterating over a vector, don’t change it. 3. … ! No runtime required.
  • 11. Credit where it is due Rust borrows liberally from other great languages: C++, Haskell, ML/Ocaml, Cyclone, Erlang, Java, C#, … Rust has an active, amazing community. ❤
  • 12. The Rust type system
  • 13. Observation Danger arises from… Aliasing Mutation Hides dependencies. Causes memory to be freed. { auto& e = v[0]; … v.push_back(…); }
  • 14. Three basic patterns fn foo(v: T) { … } Ownership fn foo(v: &T) { … } Shared borrow fn foo(v: &mut T) { … } Mutable borrow
  • 15. Ownership! ! n. The act, state, or right of possessing something.
  • 16. Aliasing Mutation Ownership (T)
  • 17. 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 a Vec<int> !
  • 18. Compiler enforces moves fn give() { let mut vec = Vec::new(); vec.push(1); vec.push(2); take(vec); vec.… push(2); } fn take(vec: Vec<int>) { // … } ! ! Error: ve! c has been moved Prevents: - use after free - double moves - …
  • 19. Borrow! ! v. To receive something with the promise of returning it.
  • 20. Aliasing Mutation Shared borrow (&T)
  • 21. Aliasing Mutation Mutable borrow (&mut T)
  • 22. fn lender() { let mut vec = Vec::new(); vec.push(1); vec.push(2); use(&vec); … } fn use(vec: &Vec<int>) { // … } ! ! ! 1 data length capacity vec 2 vec “Shared reference to Vec<int>” Loan out vec
  • 23. fn dot_product(vec1: &Vec<int>, vec2: &Vec<int>) elem1 elem2 sum -> int { let mut sum = 0; for (elem1, elem2) in vec1.iter().zip(vec2.iter()) { sum += (*elem1) * (*elem2); } return sum; } walk down matching indices elem1, elem2 are references into the vector 1 2 3 4 5 6 * vec1 vec2
  • 24. Why “shared” reference? fn dot_product(vec1: &Vec<int>, vec2: &Vec<int>) -> int {…} ! fn magnitude(vec: &Vec<int>) -> int { sqrt(dot_product(vec, vec)) } two shared references to the same vector — OK!
  • 25. Aliasing Mutation Shared references are immutable: fn use(vec: &Vec<int>) { vec.push(3); vec[1] += 2; } * Error: cannot mutate shared reference * Actually: mutation only in controlled circumstances
  • 26. Mutable references fn push_all(from: &Vec<int>, to: &mut Vec<int>) { for elem in from.iter() { to.push(*elem); } } mutable reference to Vec<int> push() is legal
  • 27. Mutable references fn push_all(from: &Vec<int>, to: &mut Vec<int>) { for elem in from.iter() { 1 2 3 to.push(*elem); } } from to elem 1 2 3 …
  • 28. What if from and to are equal? fn push_all(from: &Vec<int>, to: &mut Vec<int>) { for elem in from.iter() { 1 2 3 from to elem 1 2 3 … 1 to.push(*elem); } } dangling pointer
  • 29. 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
  • 30. { let mut vec = Vec::new(); … for i in range(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
  • 31. Abstraction! ! n. freedom from representational qualities in art.
  • 32. Rust is an extensible language • Zero-cost abstraction • Rich libraries: - Containers - Memory management - Parallelism - … • Ownership and borrowing let libraries enforce safety.
  • 33. fn example() { let x: Rc<T> = Rc::new(…); { let y = x.clone(); let z = &*y; … } // runs destructor for y } // runs destructor for x x 1 [0] y 2 1 … z Stack Heap
  • 34. Borrowing and Rc fn deref<‘a,T>(r: &’a Rc<T>) -> &’a T { … } Given a borrowed reference to an `Rc<T>`… …return a reference to the `T` inside with same extent New reference can be thought of as a kind of sublease. Returned reference cannot outlast the original.
  • 35. fn example() -> &T { let x: Rc<T> = Rc::new(…); return &*x; } // runs destructor for x Error: extent of borrow exceeds lifetime of `x`
  • 36. Concurrency! ! n. several computations executing simultaneously, and potentially interacting with each other
  • 37. Data race ✎ ✎ ✎ ✎ Two unsynchronized threads accessing same data! where at least one writes.
  • 38. Aliasing Mutation No ordering Data race Sound familiar?
  • 39. Messaging! (ownership)
  • 40. data length capacity fn parent() { let (tx, rx) = channel(); spawn(proc() {…}); let m = rx.recv(); } proc() { let m = Vec::new(); … tx.send(m); } rx tx tx m
  • 41. Shared read-only access! (ownership, borrowing)
  • 42. Arc<Vec<int>> (ARC = Atomic Reference Count) Owned, so no aliases. Vec<int> &Vec<int> ref_count data length capacity [0] [1] Shared reference, so Vec<int> is immutable.
  • 43. ✎ ✎ Locked mutable access! (ownership, borrowing)
  • 44. 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
  • 45. 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. Always data-race free
  • 46. Parallel! ! adj. occurring or existing at the same time
  • 47. Concurrent vs parallel Blocks Concurrent threads Parallel jobs
  • 48. fn qsort(vec: &mut [int]) { let pivot = vec[random(vec.len())]; let mid = vec.partition(vec, pivot); let (less, greater) = vec.mut_split_at(mid); qsort(less); qsort(greater); } let vec: &mut [int] = …; [0] [1] [2] [3] […] [n] less greater
  • 49. fn parallel_qsort(vec: &mut [int]) { let pivot = vec[random(vec.len())]; let mid = vec.partition(vec, pivot); let (less, greater) = vec.mut_split(mid); parallel::do(&[ || parallel_qsort(less), || parallel_qsort(greater) ]); } let vec: &mut [int] = …; [0] [1] [2] [3] […] [n] less greater
  • 50. Unsafe! ! adj. not safe; hazardous
  • 51. Safe abstractions fn something_safe(…) { ! unsafe { ! … ! } ! } Validates input, etc. Trust me. • Uninitialized memory • Interfacing with C code • Building parallel abstractions like ARC • …
  • 52. Status of Rust “Rapidly stabilizing.”! ! ! Goal for 1.0: - Stable syntax, core type system - Minimal set of core libraries
  • 53. Conclusions • Rust gives control without compromising safety: • Zero-cost abstractions • Zero-cost safety • Guarantees beyond dangling pointers: • Iterator invalidation in a broader sense • Data race freedom