Slide 1

Slide 1 text

1 Intro to Rust Danilo Bargen TEC Lunch & Learn, 2015-09-29

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

3 1. What is Rust?

Slide 4

Slide 4 text

4 «Rust is a systems programming language that runs blazingly fast, prevents nearly all segfaults, and guarantees thread safety.» www.rust-lang.org

Slide 5

Slide 5 text

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.

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

8 2. What is Type Safety?

Slide 9

Slide 9 text

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.

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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 IndexError: list assignment index out of range >>> - Java, JavaScript, Ruby, and Haskell are also type safe.

Slide 12

Slide 12 text

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.

Slide 13

Slide 13 text

13 3. Reading Rust

Slide 14

Slide 14 text

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 }

Slide 15

Slide 15 text

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 }

Slide 16

Slide 16 text

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 }

Slide 17

Slide 17 text

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 }

Slide 18

Slide 18 text

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 }

Slide 19

Slide 19 text

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 }

Slide 20

Slide 20 text

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 }

Slide 21

Slide 21 text

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 }

Slide 22

Slide 22 text

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 }

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

25 Example 2: Generics fn min(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.

Slide 26

Slide 26 text

26 Example 3: Generic Types struct Range { start: Idx, end: Idx, } ... Range { start: 200, end: 800 } // OK Range { start: 1.3, end: 4.7 } // Also OK

Slide 27

Slide 27 text

27 Example 4: Enumerations enum Option { Some(T), None }

Slide 28

Slide 28 text

28 Example 5: Application of Option fn safe_div(n: i32, d: i32) -> Option { if d == 0 { return None; } Some(n / d) }

Slide 29

Slide 29 text

29 Example 6: Matching an Option match safe_div(num, denom) { None => println!(“No quotient.”), Some(v) => println!(“Quotient is {}.”, v) }

Slide 30

Slide 30 text

30 Example 7: Traits trait HasArea { fn area(&self) -> f64; }

Slide 31

Slide 31 text

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) } }

Slide 32

Slide 32 text

32 Example 9: Default Methods trait Validatable { fn is_valid(&self) -> bool; fn is_invalid(&self) -> bool { !self.is_valid() } }

Slide 33

Slide 33 text

33 Example 10: Trait Composition trait Foo { fn foo(&self); } trait FooBar : Foo { fn foobar(&self); }

Slide 34

Slide 34 text

34 4. Memory Safety in Rust

Slide 35

Slide 35 text

35 Three Key Promises - No null pointer dereferences - No dangling pointers - No buffer overruns

Slide 36

Slide 36 text

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 - Error values are handled by Result - Many helpful tools to do error handling

Slide 37

Slide 37 text

37 You already saw Option fn safe_div(n: i32, d: i32) -> Option { if d == 0 { return None; } Some(n / d) }

Slide 38

Slide 38 text

38 There’s also Result enum Result { Ok(T), Err(E) }

Slide 39

Slide 39 text

39 How to use Results enum Error { DivisionByZero, } fn safe_div(n: i32, d: i32) -> Result { if d == 0 { return Err(Error::DivisionByZero); } Ok(n / d) }

Slide 40

Slide 40 text

40 Tedious Results fn do_calc() -> Result { 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) }

Slide 41

Slide 41 text

41 The try! Macro fn do_calc() -> Result { let a = try!(do_subcalc1()); let b = try!(do_subcalc2()); Ok(a + b) }

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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), }

Slide 44

Slide 44 text

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(self, f: F) -> T where F: FnOnce() -> T

Slide 45

Slide 45 text

45 Other Combinator Methods (2) Map an Option to Option or U. Option.map(self, f: F) -> Option where F: FnOnce(T) -> U Option.map_or(self, default: U, f: F) -> U where F: FnOnce(T) -> U Option.map_or_else(self, default: D, f: F) -> U where F: FnOnce(T) -> U, D: FnOnce() -> U

Slide 46

Slide 46 text

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(self, err: E) -> Result Option.ok_or_else(self, err: F) -> Result where F: FnOnce() -> E

Slide 47

Slide 47 text

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.

Slide 48

Slide 48 text

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.

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

50 Ownership: Scoping { let s = “Chuchichästli”.to_string(); } // s goes out of scope, text is freed

Slide 51

Slide 51 text

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; }

Slide 52

Slide 52 text

52 Ownership: Copy Trait { let pi = 3.1415926f32; let foo = pi; let bar = pi; // This is fine! }

Slide 53

Slide 53 text

53 Ownership: Clone Trait { let s = “Chuchichästli”.to_string(); let t1 = s.clone(); let t2 = s.clone(); }

Slide 54

Slide 54 text

54 Ownership: Deriving Copy / Clone #[derive(Copy, Clone)] struct Color { r: u8, g: u8, b: u8 }

Slide 55

Slide 55 text

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); ^

Slide 56

Slide 56 text

56 Borrowing let s = “Hello, world”.to_string(); print_with_umpff(&s); println!(“Original value was {}”, s);

Slide 57

Slide 57 text

57 Mutable Borrowing let mut s = “Hello, world”.to_string(); add_umpff(&mut s); println!(“New value is {}”, s);

Slide 58

Slide 58 text

58 Borrowing prevents moving let x = String::new(); let borrow = &x; let y = x; // error: cannot move out of `x` because // it is borrowed

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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 :)

Slide 62

Slide 62 text

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)

Slide 63

Slide 63 text

63 5. Multithreaded Programming

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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);

Slide 66

Slide 66 text

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; });

Slide 67

Slide 67 text

67 Mutexes / Arcs (2) t1.join().unwrap(); t2.join().unwrap(); let guard = data.lock().unwrap(); assert_eq!(*guard, 42);

Slide 68

Slide 68 text

68 Channels use std::sync::mpsc::channel; Signature: fn channel() -> (Sender, Receiver)

Slide 69

Slide 69 text

69 6. Further Reading

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

71 «Rust Book» Not actually a book. Official guide to learning Rust. Great resource. https://doc.rust-lang.org/book/

Slide 72

Slide 72 text

72 7. Questions

Slide 73

Slide 73 text

73 Let’s make it happen. Thank you. Contact: +41 44 542 90 60 danilo.bargen@webrepublic.ch mail@dbrgn.ch