Rust: Reach F us th es ! Nich om as Matsakis 1
Q. What is Rust? Q. Why should I care? 2
Q. What is Rust? A. High-level code, low-level performance Q. Why should I care? 3
Decisions… GC 😁 😖 😏 Control, flexibility 😲 😁 😏 Double free? 😲 😁 😏 Dangling pointers? 😲 😁 😏 Buffer overflow? 😲 😲 😏 Data races? 4
Static type system = Eat your spinach! Photo credit: Sanjoy Ghosh 5 https://www.flickr.com/photos/sanjoy/4016632253/
Static type system = Eat your spinach! Photo credit: Salim Virji 6 https://www.flickr.com/photos/salim/8594532469/
The Rust compiler just saved me from a nasty threading bug. I was working on cage (our open source development tool for Docker apps with lots of microservices), and I decided to parallelize the routine that transformed docker-compose.yml files. 7
Performance Ruby: class ::String 964K iter/sec def blank? /\A[[:space:]]*\z/ == self end end 8
static VALUE case 0x2005: Performance rb_str_blank_as(VALUE str) case 0x2006: { case 0x2007: rb_encoding *enc; case 0x2008: char *s, *e; case 0x2009: case 0x200a: enc = STR_ENC_GET(str); case 0x2028: s = RSTRING_PTR(str); case 0x2029: Ruby: if (!s || RSTRING_LEN(str) == 0) return Qtrue; case 0x202f: case 0x205f: 964K iter/sec e = RSTRING_END(str); case 0x3000: while (s < e) { #if ruby_version_before_2_2() int n; case 0x180e: unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); #endif 10x! /* found */ switch (cc) { break ; case 9: default : case 0xa: return Qfalse; case 0xb: } C: case 0xc: s += n; case 0xd: } 10.5M iter/sec case 0x20: return Qtrue; case 0x85: } case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: https://github.com/SamSaffron/fast_blank case 0x2004:
Performance Ruby: class ::String 964K iter/sec def blank? /\A[[:space:]]*\z/ == self C: end 10.5M iter/sec end extern “C” fn fast_blank(buf: Buf) -> bool { Rust: buf.as_slice().chars().all(|c| c.is_whitespace()) 11M iter/sec } Get Rust Get iterator over Are all characters string slice each character whitespace? 10
High-level, zero-cost abstractions fn is_whitespace(text: &str) -> bool { text.chars() .all(|c| c.is_whitespace()) } fn load_images(paths: &[PathBuf]) -> Vec<Image> { paths.par_iter() .map(|path| Image::load(path)) .collect() } 11
Q. What is Rust? A. High-level code, low-level performance Q. Why should I care? A. You can do more! 12
Experienced C++ hacker? Make and maintain the designs you always wanted — but could not justify. Prefer Ruby? JavaScript? Tune up your application and address hot-spots. Lower memory usage. Add threads without fear. 13
I like Rust because it is boring . — CJ Silverio, npm CTO 14
Design of Rust Rust in Production Rust Community 15
“Must be this tall to write multi-threaded code” David Baron Mozilla Distinguished Engineer 16
Data races Actor-based languages Sharing (e.g., Erlang, WebWorkers) Functional languages Mutation (e.g., Haskell) No ordering Sequential programming Data race 17
Data races Sharing Rust: No sharing and mutation at the same time. Mutation No ordering Data race 18
Ownership and Borrowing Photo Credit: Nathan Kam https://www.youtube.com/watch?v=Tnssn9KcWLg 19
~ Ownership and borrowing ~ Type Ownership Alias? Mutate? ✓ T Owned
fn main() { fn eat(apple: Apple) { let apple = Apple::new(); … eat(apple); } Give ownership eat(apple); of the apple. ~~~~~~~~~ Take ownership } Error: `apple` has of the apple been moved. Owns Owns 21
fn main() { fn deliver(bag: Vec<Apple>) { let apple = Apple::new(); … let mut bag = Vec::new(); } bag.push(apple); Give ownership. Take ownership bag.push(Apple::new()); deliver(bag); of the vector Give ownership. } Owns Owns 22
“Manual” memory management in Rust: Values owned by creator . ] Feels invisible. Values moved via assignment. When final owner returns, value is freed . 23
~ Ownership and borrowing ~ Type Ownership Alias? Mutate? ✓ T Owned ✓ &T Shared reference
fn main() { fn weigh(bag: &Vec<Apple>) -> u32 { let apple = Apple::new(); … let mut bag = Vec::new(); } bag.push(apple); Shared reference bag.push(Apple::new()); to the vector let weight = weigh(&bag); … (Return type) } Loan out the bag shared references 25
Sharing “freezes” data (temporarily) let mut bag = Vec::new(); bag.push(…); `bag` mutable here let r = &bag; `bag` borrowed here bag.len(); reading `bag` ok while shared cannot mutate while shared bag.push(…); ~~~~~~~~~~~ cannot mutate through shared ref r.push(…); ~~~~~~~~~ after last use of `r`, bag.push(…); `bag` is mutable again 26
~ Ownership and borrowing ~ Type Ownership Alias? Mutate? ✓ T Owned ✓ &T Shared reference ✓ &mut T Mutable reference
Mutable references: no other access let mut bag = Vec::new(); bag.push(…); `bag` mutable here let r = & mut bag; `bag` mutably borrowed here bag.len(); cannot access `bag` while borrowed ~~~~~~~~~ r.push(…); but can mutate through `r` bag.push(…); after last use of `r`, `bag` is accessible again 28
Parallelism Photo credit: Dave Gingrich 29 https://www.flickr.com/photos/ndanger/2744507570/
Observation: Building parallel abstractions is easy. Misusing those abstractions is also easy. func foo(…) { m := make(map[string]string) Go Code m[“Hello”] = “World” send data over channel channel <- m m[“Hello”] = “Data Race” but how to stop sender from } using it afterwards? 30
fn foo(…) { impl <T> Channel<T> { let m = HashMap::new(); fn send(& mut self , data: T) { m.insert(“Hello”, “World”); … channel.send(m); } m.insert(“Hello”, “Data Race”); } ~~~~~~~~~~~~~~~~~~~~~~~~~~ } Take ownership of the data Error: use of moved value: `book` 31
~ Concurrency paradigms ~ Paradigm Ownership? Borrowing? ✓ Message passing Fork join ✓
borrowed from caller fn load_images(paths: &[PathBuf]) -> Vec<Image> { paths.iter() For each path… .map(|path| { Image::load(path) …load an image… }) .collect() …create and return a vector. } paths = [ “a.jpg”, “b.png”, …, “c.jpg” ] 33
extern crate rayon; Third-party library fn load_images(paths: &[PathBuf]) -> Vec<Image> { paths.par_iter() Make it parallel .map(|path| { Image::load(path) }) .collect() } paths = [ “a.jpg”, “b.png”, …, “c.jpg” ] 34
Observation: Building parallel abstractions is easy. Misusing those abstractions is also easy. 35
fn load_images(paths: &[PathBuf]) -> Vec<Image> { let mut jpgs = 0; How many jpgs seen so far? paths.par_iter() If current file name ends in “jpg”… .map(|path| { if path.ends_with(“.jpg”) { jpgs += 1; } Image::load(path) …add 1 to the counter. }) .collect() } 0 0 + 1 + 1 0 1 1 1 36
fn load_images(paths: &[PathBuf]) -> Vec<Image> { let mut jpgs = 0; paths.par_iter() borrows .map( ) borrows mutably .collect() mutably } |path| { |path| { if path.ends_with(“.jpg”) { if path.ends_with(“.jpg”) { ~~~~~~~~~ ❌ ~~~~~~~~~ ❌ jpgs += 1; jpgs += 1; } } Image::load(path) Image::load(path) } } 37
~ Concurrency paradigms ~ Paradigm Ownership? Borrowing? ✓ Message passing Fork join ✓ ✓ ✓ Locking ✓ ✓ Lock-free Futures ✓ ✓ …
Unsafe 39
Rust: An Extensible Language File R e f e m r e s n c i e l System e l c l o a u r Libraries n a t P Core Language Ownership and Borrowing 40
Safe abstractions fn split_at_mut(…) { unsafe { … } Validates input, etc. } Trust me. Ownership/borrowing/traits give tools to enforce safe abstraction boundaries. 41
Stylo (Parallel CSS Rendering — coming in FF57) Total KLOC Unsafe KLOC Unsafe % Total 146.2 51.7 35% Interesting stuff 71.6 1.4 1.9% FFI Bindings 74.5 50.3 67.4% 42
Integration Photo credit: Don Urban 43 https://www.flickr.com/photos/donpezzano/3044965125/
node.js Ruby neon-bindings/neon usehelix.com CPython PyO3/pyo3 dgrunwald/rust-cpython getsentry/milksnake 44
Worker ArrayBuffer 45
fn callback( mut vm: CallContext) { ArrayBuffer let buffer = vm.arguments(0); let guard: VmGuard = vm.lock(); let data = buffer.borrow_mut(&guard); data .par_iter_mut() buffer .for_each(|i| *i += 1); data } borrows vm guard data 46
ArrayBuffer What could go wrong? What if we invoked JS callback while using `data`? ▶ Can’t: Invoking JS callbacks requires mutable buffer borrow of `vm`! data borrow vm guard data 47
https://medium.com/@wireapp/3ff37fc98c3f 48
ruby! { class Console { def log(string: &str) { println!("LOG: {:?}", string); } } } 49
Rust in Production 50
Will discuss today! Whitepapers available online. rust-lang.org/en-US/whitepapers.html 51
Category: Experienced system devs 52
53
54
Recommend
More recommend