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

And thou shalt have rigour: a gentle introducti...

And thou shalt have rigour: a gentle introduction to Rust

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

Avatar for Alberto Fernández

Alberto Fernández

December 15, 2015
Tweet

More Decks by Alberto Fernández

Other Decks in Programming

Transcript

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

    - 10 min • Demo - 5 min Agenda 3
  2. - 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
  3. - 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
  4. - Pattern matching - Move semantics - Option and Result,

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

    - Steep learning curve - No C++ bindings :( - Somewhat small community, growing every day! Rust: the bad 7
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. // 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
  18. 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
  19. 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
  20. 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
  21. // 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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)
  29. 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
  30. 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!
  31. 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