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

And thou shalt have rigour: a gentle introduction to Rust

And thou shalt have rigour: a gentle introduction to Rust

Rust is a memory safe, compiled language. In this talk we introduce the language.

5af538a41de373ba69d6c0c103bfa518?s=128

Alberto Fernández

December 15, 2015
Tweet

Transcript

  1. And thou shalt have rigour A gentle introduction to Rust

    1
  2. Disclaimer I’m no expert! 2

  3. • Introduction to language - 20 min • Code Walking

    - 10 min • Demo - 5 min Agenda 3
  4. - Safe, concurrent, systems programming language - 1.0: May 2015,

    still young! - LLVM backend - Focused on being memory safe - Many, many features! Rust: an overview 4
  5. - Guaranteed memory safety! - No GC - No data

    races - Generics - Traits - Minimal runtime - C bindings - Strongly typed, but with type inference - Excepcional standard library, many zero-cost abstractions, including concurrency primitives Rust: the good 5
  6. - Pattern matching - Move semantics - Option and Result,

    no exceptions - Most advanced ownership system out there - Compile time borrow checker - Lifetimes Rust: the good 6
  7. - Lots of syntax - Memory model difficult to grasp

    - Steep learning curve - No C++ bindings :( - Somewhat small community, growing every day! Rust: the bad 7
  8. C++ int main() { std::vector<std::string> v; v.push_back(“Hello"); std::string& x =

    v[0]; v.push_back(“world"); std::cout << x; } Borrow checker sample Rust fn main() { let mut v = vec![]; v.push(“Hello"); let w = &v[0]; v.push(“world"); println!("{} world", w); } 8
  9. C++ g++ borrow.cpp -o borrow *[1] 1827 segmentation fault ./borrow

    valgrind ./borrow ==13521== HEAP SUMMARY: ==13521== in use at exit: 39,759 bytes in 429 blocks ==13521== total heap usage: 510 allocs, 81 frees, 45,991 bytes allocated ==13521== ==13521== LEAK SUMMARY: ==13521== definitely lost: 0 bytes in 0 blocks ==13521== indirectly lost: 0 bytes in 0 blocks ==13521== possibly lost: 0 bytes in 0 blocks ==13521== still reachable: 4,096 bytes in 1 blocks ==13521== suppressed: 35,663 bytes in 428 blocks ==13521== Rerun with --leak-check=full to see details of leaked memory ==13521== ==13521== For counts of detected and suppressed errors, rerun with: -v ==13521== ERROR SUMMARY: 8 errors from 4 contexts (suppressed: 0 from 0) Borrow checker sample 9
  10. ¡Qué desastre! Borrow checker sample 10

  11. Borrow checker sample borrow.rs:8:5: 8:6 error: cannot borrow `v` as

    mutable because it is also borrowed as immutable borrow.rs:8 v.push("world"); ^ borrow.rs:6:11: 6:12 note: previous borrow of `v` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `v` until the borrow ends borrow.rs:6 let w = &v[0]; ^ borrow.rs:11:2: 11:2 note: previous borrow ends here borrow.rs:1 fn main() { ... borrow.rs:11 } ^ error: aborting due to previous error 11
  12. Not even compiles! Borrow checker sample 12

  13. fn main() { // Variables can be type annotated. let

    logical: bool = true; let a_float: f64 = 1.0; // Regular annotation let an_integer = 5i32; // Suffix annotation // Or a default will be used. let default_float = 3.0; // `f64` let default_integer = 7; // `i32` let tuple = (1u8, 2u16, 3u32, 4u64, -1i8, -2i16, -3i32, -4i64, 0.1f32, 0.2f64, 'a', true); // Values can be extracted from the tuple using tuple indexing println!("long tuple first value: {}", long_tuple.0); } Primitives 13
  14. Array and slices fn main() { let xs: [i32; 5]

    = [1, 2, 3, 4, 5]; // All elements can be initialized to the same value let ys: [i32; 500] = [0; 500]; println!("first element of the array: {}", xs[0]); println!("array size: {}", xs.len()); // Arrays can be automatically borrowed as slices println!("borrow the whole array as a slice"); analyze_slice(&xs); println!("borrow a section of the array as a slice"); analyze_slice(&ys[1 .. 4]); // borrow a section of the slice! } fn analyze_slice(slice: &[i32]) { println!("first element of the slice: {}", slice[0]); println!("the slice has {} elements", slice.len()); } Arrays and slices 14
  15. struct Point { x: f64, y: f64, } // instantiate

    let point: Point = Point { x: 1, y: 1} // or with type inference let point = Point { x: 1, y: 1} let x = Point.x; Structs and enums 15
  16. enum Person { // An `enum` may either be `unit-like`

    (like C) Skinny, Fat, // like tuple structs, Height(i32), Weight(i32), // or like structures. Info { name: String, height: i32 } } fn main() { let person = Person::Height(18); let danny = Person::Weight(10); let dave = Person::Info {name: "Dave".to_owned(), height: 72}; let john = Person::Fat; let larry = Person::Skinny; } Structs and enums 16
  17. if n < 0 { // do stuff! } //

    if are expressions! let n_less_than_0 = if n < 0 { true }; // while and for/range while n < 0 { // do stuff! } for i in range 1..100 { // do 100 stuffs! } loop { // infinite loop } Flow control 17
  18. let number = 13; match number { 1 => println!("One!"),

    // match single 2 | 3 | 5 | 7 | 11 => println!("This is a prime"), // match OR 13...19 => println!("A teen"), // match range _ => println!("Ain't special"), // rest of cases } let boolean = true; // match is an expression too! let binary: u8 = match boolean { false => 0, true => 1, }; let pair = (0, -2); // match works with tuples, enums, structs, etc aswell (destructuring) match pair { (0, y) => println!("First is `0` and `y` is `{:?}`", y), (x, 0) => println!("`x` is `{:?}` and last is `0`", x), } Flow control: pattern matching 18
  19. fn sample_function(s: &str, p: Point) -> bool { true //

    when no ";", this will be returned } struct Point { x: f64, y: f64, } impl Point { fn new(x: f64, y: f64) -> Point { Point { x: x, y: y } } fn do_something(&mut self) -> bool { self.x = 10; return true; // can also use "return" } } let point = Point::new(); point.do_something() sample_function("", point); Functions and methods 19
  20. let convert_bin_to_bool = |number: u8| -> bool { match number

    { 1 => true, 0 => false, _ => panic!("No binary number!"), } }; let my_number = 0; println!("This is: {}", convert_bin_to_bool(my_number)); Closures 20
  21. // in functions fn im_generic<T>(s: Vec<T>) { } // in

    structs struct GenericStruct<T>; // specialization impl GenericStruct<i32> { } // generic impl <T> GenericStruct<T> { } Generics 21
  22. struct MySqlDriver { host: String, username: String, password: String, }

    trait DatabaseDriver { // static fn new() -> Self; // instance methods fn connect(&self) -> bool; fn do_something(&self, data: i32) -> i32; // default implementation fn name(&self) -> &'static str { "driver" } } Traits impl DatabaseDriver for MySqlDriver { fn new() { MySqlDriver { ... } } fn connect(&self) { self.do_connect(self.host, ...); } fn do_something(&self, data: i32) { ... } } impl MySqlDriver { fn another_method_not_in_trait() { ... } } 22
  23. macro_rules! say_hello { () => ( // <- these are

    macro arguments println!("Hello!"); ) } fn main() { // this call will expand into `println!("Hello");` say_hello!() // built-in macros println!("hello there"); panic!("this is shit"); // used for testing assert!(5 > 6); assert_eq!(true, false); } Macros 23
  24. mod my_module { fn test() { println!("this is private"); }

    pub fn test_public() { println!("this is public!"); } pub mod recursive { pub fn im_deep_inside() { println!("indeed"); } } } fn main () { my_module::test_public(); // <- works my_module::recursive::im_deep_inside(); // indeed! my_module::test(); // no way men } Modules 24
  25. // example: test #[test] fn this_is_a_test() { ... } #[cfg(target_os

    = "linux")] fn are_you_on_linux() { println!("You are running linux!") } #[cfg(not(target_os = "linux"))] fn are_you_on_linux() { println!("You are *not* running linux!") } // also as macro! if cfg!(target_os = "linux") { println!("Yes. It's definitely linux!"); } else { println!("Yes. It's definitely *not* linux!"); } Attributes 25
  26. enum Result<T, E> { Ok(T), Err(E) } fn my_function() ->

    Result<String, i32> { if some_condition { Ok("Here is your data".to_string()) } else { Err(100) } } match my_function() { Ok(v) => println!("{}", v), Err(e) => println!("This is a disaster: {}", e), } // also try! this will panic when Err is found! let my_ok_value = try!(my_function()); Result 26
  27. pub enum Option<T> { None, Some(T), } fn divide(numerator: f64,

    denominator: f64) -> Option<f64> { if denominator == 0.0 { None } else { Some(numerator / denominator) } } let result = divide(2.0, 3.0); // -> Option! match result { Some(x) => println!("Result: {}", x), None => println!("Cannot divide by 0"), } Option 27
  28. fn destroy_box(c: Box<i32>) { // now I own Box! }

    // Box will be destroyed out of the scope here fn main() { let a = Box::new(100); println!("a contains: {}", a); let b = a; // now b owns the Box println!("a contains: {}", a); // ERROR destroy_box(b); // moving Box to the function println!("b contains: {}", b); // ERROR } Move semantics 28
  29. fn eat_box(boxed_int: Box<i32>) { println!("destroying box that contains {}", boxed_int);

    } // box goes out of scope, destroy! fn peep_inside_box(borrowed_int: &i32) { println!("This int is: {}", borrowed_int); } fn main() { let boxed_int = Box::new(5); peep_inside_box(&boxed_int); // ownership is intact! { let _boxed_int_ref: &i32 = &boxed_int; eat_box(boxed_int); // ERROR } // boxed_int_ref goes out of scope, destroy! eat_box(boxed_int); } Borrowing 29
  30. Lifetimes 30 https://play.rust-lang.org/? gist=3b670d9b30d232ac644c&version=stable

  31. use std::collections::HashMap; let mut book_reviews = HashMap::new(); // type inference!

    book_reviews.insert("Whatever", "Awesome!"); book_reviews.contains_key("Les Mis√érables"); book_reviews.remove("Whatever"); std: collections 31 • Sequences: Vec, VecDeque, LinkedList • Maps: HashMap, BTreeMap • Sets: HashSet, BTreeSet • Misc: BinaryHeap
  32. fn main() { let boxed_data: Box<i32> = Box::new(1000); // remove

    layer of indirection // copied to the stack! let raw_data = *boxed_data; } // I'm cleaned here std: Box 32 • Heap allocated data • Like “unique_ptr<T>” in C++ • Memory automatically freed when out-of-scope
  33. use std::sync::Arc; use std::thread; let numbers: Vec<_> = (0..100u32).collect(); let

    shared_numbers = Arc::new(numbers); for _ in 0..10 { let child_numbers = shared_numbers.clone(); // move 'child_numbers' to the thread, work locally thread::spawn(move || { let local_numbers = &child_numbers[..]; }); } std::sync 33 • Multiple sync primitives: Arc, Barrier, Mutex, Once, etc. • Also mpsc (channels)
  34. use std::thread; use std::sync::mpsc::channel; // Create a simple streaming channel

    let (tx, rx) = channel(); thread::spawn(move|| { tx.send(10).unwrap(); }); assert_eq!(rx.recv().unwrap(), 10); std::mpsc (channels) 34 • Multi-producer, single-consumer FIFO queue communication primitives • Like Go but a little bit more syntax
  35. std: many other things 35 • Atomics: AtomicBool, AtomicPtr, etc.

    • std::cmp, comparisons: Eq, PartialEq, etc. • IO: std::io, std::net (TcpListener, etc.) • Iterators: Chain, Cycle, Once, Map, etc. • Processes: std::process, run command, pipes, etc. • Most of these expose traits, so you can extend your structures this way!
  36. Code Walking 36 • Redis clone • Made to illustrate

    introductory Rust usage • Fully commented and unit tested • Functional: GET, SET, DEL, EXISTS, SADD, SREM, SISMEMBER, SMEMBERS • Just clone, make, and enjoy! • https://github.com/albertofem/carcasian
  37. Demo 37 Let’s put Rust to real world use!

  38. Links 38 Official page: http://rust-lang.org Book: http://doc.rust-lang.org/book/ Reference: https://doc.rust-lang.org/stable/reference.html Cargo

    crates: https://crates.io/ Rust by example: http://rustbyexample.com
  39. println!(“{}”, “The End!”); 39 Q&A