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

Intro to Rust

Avatar for dbrgn 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

Avatar for dbrgn

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/