Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Intro to Rust

dbrgn
September 29, 2015

Intro to Rust

An introduction to the Rust programming language, based on the free "Why Rust" book by Jim Blandy (http://www.oreilly.com/programming/free/why-rust.csp).

This was a company-internal presentation at Webrepublic AG.

The version with speaker notes can be found here: https://speakerdeck.com/dbrgn/intro-to-rust-with-speaker-notes

dbrgn

September 29, 2015
Tweet

More Decks by dbrgn

Other Decks in Technology

Transcript

  1. 2 Agenda 1. What is Rust? 2. What’s Type Safety?

    3. Reading Rust 4. Memory Safety in Rust 5. Multithreaded Programming 6. Further Reading 7. Questions
  2. 4 «Rust is a systems programming language that runs blazingly

    fast, prevents nearly all segfaults, and guarantees thread safety.» www.rust-lang.org
  3. 5 What’s wrong with systems languages? - It’s difficult to

    write secure code. - It’s very difficult to write multithreaded code. These are the problems Rust was made to address.
  4. 6 Quick Facts about Rust (As of September 2015) -

    Started by Mozilla employee Graydon Hoare - First announced by Mozilla in 2010 - Community driven development - First stable release: 1.0 in May 2015 - Latest stable release: 1.3 - 46'484 commits on Github - Largest project written in Rust: Servo
  5. 7 Features - Zero-cost abstractions - Move semantics - Guaranteed

    memory safety - Threads without data races - Trait based generics - Pattern matching - Type inference - Minimal runtime, no GC - Efficient C bindings
  6. 9 A C Program int main(int argc, char **argv) {

    unsigned long a[1]; a[3] = 0x7ffff7b36cebUL; return 0; } According to C99, undefined behavior. Output: undef: Error: .netrc file is readable by others. undef: Remove password or make file unreadable by others.
  7. 10 Definitions - If a program has been written so

    that no possible execution can exhibit undefined behavior, we say that program is well defined. - If a language’s type system ensures that every program is well defined, we say that language is type safe
  8. 11 Type Safe Languages - C and C++ are not

    type safe. - Python is type safe: >> a = [0] >>> a[3] = 0x7ffff7b36ceb Traceback (most recent call last): File "", line 1, in <module> IndexError: list assignment index out of range >>> - Java, JavaScript, Ruby, and Haskell are also type safe.
  9. 12 It’s ironic. C and C++ are not type safe.

    Yet they are being used to implement the foundations of a system. Rust tries to resolve that tension.
  10. 14 Example 1 fn gcd(mut n: u64, mut m: u64)

    -> u64 { assert!(n != 0 && m != 0); while m != 0 { if m < n { let t = m; m = n; n = t; } m = m % n; } n }
  11. 15 Example 1 fn gcd(mut n: u64, mut m: u64)

    -> u64 { assert!(n != 0 && m != 0); while m != 0 { if m < n { let t = m; m = n; n = t; } m = m % n; } n }
  12. 16 Example 1 fn gcd(mut n: u64, mut m: u64)

    -> u64 { assert!(n != 0 && m != 0); while m != 0 { if m < n { let t = m; m = n; n = t; } m = m % n; } n }
  13. 17 Example 1 fn gcd(mut n: u64, mut m: u64)

    -> u64 { assert!(n != 0 && m != 0); while m != 0 { if m < n { let t = m; m = n; n = t; } m = m % n; } n }
  14. 18 Example 1 fn gcd(mut n: u64, mut m: u64)

    -> u64 { assert!(n != 0 && m != 0); while m != 0 { if m < n { let t = m; m = n; n = t; } m = m % n; } n }
  15. 19 Example 1 fn gcd(mut n: u64, mut m: u64)

    -> u64 { assert!(n != 0 && m != 0); while m != 0 { if m < n { let t = m; m = n; n = t; } m = m % n; } n }
  16. 20 Example 1 fn gcd(mut n: u64, mut m: u64)

    -> u64 { assert!(n != 0 && m != 0); while m != 0 { if m < n { let t = m; m = n; n = t; } m = m % n; } n }
  17. 21 Example 1 fn gcd(mut n: u64, mut m: u64)

    -> u64 { assert!(n != 0 && m != 0); while m != 0 { if m < n { let t = m; m = n; n = t; } m = m % n; } n }
  18. 22 Example 1 fn gcd(mut n: u64, mut m: u64)

    -> u64 { assert!(n != 0 && m != 0); while m != 0 { if m < n { let t = m; m = n; n = t; } m = m % n; } n }
  19. 23 Example 2: Generics fn min<T: Ord>(a: T, b: T)

    -> T { if a <= b { a } else { b } }
  20. 24 Example 2: Generics fn min<T: Ord>(a: T, b: T)

    -> T { if a <= b { a } else { b } }
  21. 25 Example 2: Generics fn min<T: Ord>(a: T, b: T)

    -> T { if a <= b { a } else { b } } ... min(10i8, 20) == 10; // T is i8 min(10, 20u32) == 10; // T is u32 min(“abc”, “xyz”) == “abc”; // Strings are Ord min(10i32, “xyz”); // error: mismatched types.
  22. 26 Example 3: Generic Types struct Range<Idx> { start: Idx,

    end: Idx, } ... Range { start: 200, end: 800 } // OK Range { start: 1.3, end: 4.7 } // Also OK
  23. 28 Example 5: Application of Option<T> fn safe_div(n: i32, d:

    i32) -> Option<i32> { if d == 0 { return None; } Some(n / d) }
  24. 29 Example 6: Matching an Option match safe_div(num, denom) {

    None => println!(“No quotient.”), Some(v) => println!(“Quotient is {}.”, v) }
  25. 31 Example 8: Trait Implementation struct Circle { x: f64,

    y: f64, radius: f64, } impl HasArea for Circle { fn area(&self) -> f64 { consts::PI * (self.radius * self.radius) } }
  26. 32 Example 9: Default Methods trait Validatable { fn is_valid(&self)

    -> bool; fn is_invalid(&self) -> bool { !self.is_valid() } }
  27. 33 Example 10: Trait Composition trait Foo { fn foo(&self);

    } trait FooBar : Foo { fn foobar(&self); }
  28. 35 Three Key Promises - No null pointer dereferences -

    No dangling pointers - No buffer overruns
  29. 36 P1: No null pointer dereferences - Null pointers are

    useful - They can indicate the absence of optional information - They can indicate failures - But they can introduce severe bugs - Rust separates the concept of a pointer from the concept of an optional or error value - Optional values are handled by Option<T> - Error values are handled by Result<T, E> - Many helpful tools to do error handling
  30. 37 You already saw Option<T> fn safe_div(n: i32, d: i32)

    -> Option<i32> { if d == 0 { return None; } Some(n / d) }
  31. 39 How to use Results enum Error { DivisionByZero, }

    fn safe_div(n: i32, d: i32) -> Result<i32, Error> { if d == 0 { return Err(Error::DivisionByZero); } Ok(n / d) }
  32. 40 Tedious Results fn do_calc() -> Result<i32, String> { let

    a = match do_subcalc1() { Ok(val) => val, Err(msg) => return Err(msg), } let b = match do_subcalc2() { Ok(val) => val, Err(msg) => return Err(msg), } Ok(a + b) }
  33. 41 The try! Macro fn do_calc() -> Result<i32, String> {

    let a = try!(do_subcalc1()); let b = try!(do_subcalc2()); Ok(a + b) }
  34. 42 Mapping Errors fn do_subcalc() -> Result<i32, String> { …

    } fn do_calc() -> Result<i32, Error> { let res = do_subcalc(); let mapped = res.map_err(|msg| { println!(“Error: {}”, msg); Error::CalcFailed }); let val = try!(mapped); Ok(val + 1) }
  35. 43 Mapping Errors let mapped = res.map_err(|msg| Error::CalcFailed); is the

    same as let mapped = match res { Ok(val) => Ok(val), Err(msg) => Err(Error::CalcFailed), }
  36. 44 Other Combinator Methods (1) Get the value from an

    option. Option.unwrap(self) -> T Option.unwrap_or(self, def: T) -> T Option.unwrap_or_else<F>(self, f: F) -> T where F: FnOnce() -> T
  37. 45 Other Combinator Methods (2) Map an Option<T> to Option<U>

    or U. Option.map<U, F>(self, f: F) -> Option<U> where F: FnOnce(T) -> U Option.map_or<U, F>(self, default: U, f: F) -> U where F: FnOnce(T) -> U Option.map_or_else<U, D, F>(self, default: D, f: F) -> U where F: FnOnce(T) -> U, D: FnOnce() -> U
  38. 46 Other Combinator Methods (3) Convert an option to a

    result, mapping Some(v) to Ok(v) and None to Err(err). Option.ok_or<E>(self, err: E) -> Result<T, E> Option.ok_or_else<E, F>(self, err: F) -> Result<T, E> where F: FnOnce() -> E
  39. 47 P2: No dangling pointers - Rust programs never try

    to access a heap-allocated value after it has been freed. - No garbage collection or reference counting involved! - Everything is enforced at compile time.
  40. 48 Three Rules - Rule 1: Every value has a

    single owner at any given time. - Rule 2: You can borrow a reference to a value, so long as the reference doesn’t outlive the value. - Rule 3: You can only modify a value when you have exclusive access to it.
  41. 49 Ownership - Variables own their values - A struct

    owns its fields - An enum owns its values - Every heap-allocated value has a single pointer that owns it - All values are dropped when their owner is dropped
  42. 51 Ownership: Move Semantics { let s = “Chuchichästli”.to_string(); //

    t1 takes ownership from s let t1 = s; // compile-time error: use of moved value s let t2 = s; }
  43. 52 Ownership: Copy Trait { let pi = 3.1415926f32; let

    foo = pi; let bar = pi; // This is fine! }
  44. 55 But what about this? let s = “Hello, world”.to_string();

    print_with_umpff(s); println!(“{}”, s); error: use of moved value: `s` println!(“{}”, s); ^ note: `s` moved here because it has type `collections::string:: String`, which is non-copyable print_with_umpff(s); ^
  45. 58 Borrowing prevents moving let x = String::new(); let borrow

    = &x; let y = x; // error: cannot move out of `x` because // it is borrowed
  46. 59 Lifetimes let borrow; let x = String::new(); borrow =

    &x; // error: `x` does not live // long enough
  47. 60 Lifetimes { let borrow; { let x = String::new();

    borrow = &x; // error: `x` does not live // long enough } }
  48. 61 Lifetimes - Sometimes the compiler is wrong about automatically

    inferred lifetimes - He needs more knowledge - Parameters and return values can be annotated with explicit lifetimes - Won’t be covered here :)
  49. 62 P3: No buffer overruns - There’s no pointer arithmetic

    in Rust - Arrays in Rust are not just pointers - Bounds checks, usually at compile time (zero cost abstractions)
  50. 64 We’ll make this short - The Rust compiler does

    not know about concurrency - Everything works based on the three rules - I’ll only show a few examples
  51. 65 Threads let t1 = std::thread::spawn(|| { return 23; });

    let t2 = std::thread::spawn(|| { return 19; }); let v1 = try!(t1.join()); let v2 = try!(t2.join()); println!(“{} + {} = {}”, v1, v2, v1 + v2);
  52. 66 Mutexes / Arcs (1) let data = Arc::new(Mutex::new(0)); let

    data1 = data.clone(); let t1 = thread::spawn(move || { let mut guard = data1.lock().unwrap(); *guard += 19; }); let data2 = data.clone(); let t2 = thread::spawn(move || { let mut guard = data2.lock().unwrap(); *guard += 23; });
  53. 70 «Why Rust?» Free e-book by O’Reilly, ~50 pages. Highly

    recommended! This presentation is actually based on that book. http://www.oreilly.com/programming/free/why-rust.csp
  54. 71 «Rust Book» Not actually a book. Official guide to

    learning Rust. Great resource. https://doc.rust-lang.org/book/