class: center, middle # The Tale of Troy
Aloïs Cochard @aloiscochard --- # About me You can find me online (github, twitter, irc...) as `aloiscochard`. #### Open Source Contributor - Haskell - sarsi, codex, codec-jvm ([eta-lang.org](http://eta-lang.org)), machines-*, ... - Scala - scalaz, scato, ... - Rust - Coming Soon! #### Engineer @ Swisscom (CH) Apply machine learning on telco mobility (big) data. More details at https://t.co/CM3zPPIpXX ??? I'm here to talk about the programming language **Rust**. - Who never heard of it before? - Who already wrote some programs with Rust? --- # Introduction Rust is a systems programming language in the vain of `C` or `C++` ```rust fn main() { println!("Hello, world!"); } ``` ??? Survey: - Who can read C? - Who can write C? - Who think can write safe C code? --- # Introduction Rust is a systems programming language in the vain of `C` or `C++` ```rust fn main() { println!("Hello, world!"); } ``` While retaining C-like syntax it incorporate a lots of cool new features: -
Pattern matching and algebraic data types
-
Higher-order functions
-
Immutable by default
- Task-based concurrency - Deterministic management of resources - ... #
as fast as C++
??? What does that means? - Motivate learning Rust. - Doing system programming - Writing OS, Drivers, VM, Distributed Systems, ML, ... - Writing GC-less code - ... Share my own experience: - Stuff I always wanted to do but kept aside - Google GO-Lang was such a disapointment --- # Agenda - History - Functional Rust - DysFunctional Rust - Practical Rust
>> **Rust** *is a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety.*
https://www.rust-lang.org --- ### The Book of Mozilla, 11:14
.left[The Beast adopted new raiment and studied the ways of Time and Space and Light and the Flow of energy through the Universe.] .left[From its studies, the Beast fashioned new structures from **oxidised metal** and proclaimed their glories.] .left[And the Beast's followers rejoiced, finding renewed purpose in these teachings.]
.left[https://en.wikipedia.org/wiki/The_Book_of_Mozilla] --- # History 2006 - Inception by Graydon Hoare 2009 - Mozilla sponsorship 2010 - Initial release 2011 - Compiler bootstrapping 2012 - Inception of [Servo](https://github.com/servo/servo/commit/783455f684bc613dba25d5c2e54157c336093f35) >> **Servo** is a modern, high-performance browser engine designed for both application and embedded use. 2017 - Release of [Quantum](https://wiki.mozilla.org/Quantum) >> Bringing stable portions of **Servo** into **Firefox**. ??? Firefox Quantum: - They manage to implement fine grained parallelism at first try, after *multiple* failed attempt in C++ --- # Functional Rust --- # Functional Rust ##
Immutable by default
```rust let i = 3; i = i + 1; ``` ```rust fn f(i: i8) -> () { i = i + 1; } ``` .red[error: cannot assign twice to immutable variable `i`] ```rust let mut i = 3; i += 1; ``` --- # Functional Rust ##
First class functions
```rust let f = |a| a + 1; assert!(43 == f(42)); ``` ??? Local type inference did kick-in to resolve the type of `f` --- # Functional Rust ##
First class functions
```rust let f: fn(i8) -> i8 = |a| a + 1; assert!(43 == f(42)); ``` --- # Functional Rust ##
Closures
```rust let x = 1; let f = |a| a + x; assert!(43 == f(42)); ``` --- # Functional Rust ##
Closures
```rust let x = 1; let f: fn(i8) -> i8 = |a| a + x; assert!(43 == f(42)); ``` .red[error: mismatched types] .red[ > expected `fn(i8) -> i8`
found `[closure@src/...: x:_]` ] Type got infered based on the relationship with the enclosing environment. ??? By default functions are not heap allocated, ... --- # Functional Rust ##
Higher-order functions
```rust fn hof(i: i8, f: fn(i8) -> i8) -> i8 { f(f(i)) } let f: fn(i8) -> i8 = |a| a + 1; let y = hof(42, f); assert!(44 == y); ``` ??? But what if we want to pass a closure here? --- # Functional Rust ##
Higher-order functions
```rust fn hof
(i: i8, f: F) -> i8 where F: Fn(i8) -> i8 { f(f(i)) } let x = 1; let f = |a| a + x; let y = hof(42, f); assert!(44 == y); ``` Using lambda expression: ```rust let y = hof(42, |a| a + x); assert!(44 == y); ``` .center[No heap allocations happening here!] ??? Using parametric function type, we get polymorphism and let the compiler optimize properly. Avoiding heap allocation entierly. But what if we want to hand out a closure as output? --- # Functional Rust ##
Higher-order functions
```rust fn hof
(i: i8, f: F) -> i8 where F: Fn(i8) -> i8 { f(f(i)) } fn mk_f(x: i8) -> Box
i8> { let f = move |a| a + x; Box::new(f) } ``` ```rust let x = 1; let f = mk_f(x); let y = hof(42, f.as_ref()); // From Box
to &T assert!(44 == y); ``` ??? Box trigger heap allocation, like a traditional closure in other languages. --- # Functional Rust ##
Iterators
```rust let xs: Vec
= vec![1, 2, 3, 4]; let ys: Vec
= vec![2, 4, 6, 8]; let zs: Vec
= xs.iter() .zip(ys.iter()) .map(|(i, c)| i * c) .filter(|&x| x > 10) .collect(); assert!(zs == vec![18, 32]); ``` ??? Iterator are evaluated lazy, here nothing happens before we call `collect`. - This whole computation is done in only one pass (Fusion) --- # Functional Rust ##
Algebraic data types
```rust enum Maybe
{ Some(A), None } ``` --- # Functional Rust ##
Algebraic data types
```rust enum Maybe
{ Some(A), None } ``` ```rust let x = Some(42); let is_defined = match x { None => false, //Some(_) => true, }; assert!(is_defined == true); ``` .red[error: non-exhaustive patterns: `Some(_)` not covered] --- # Functional Rust ##
Algebraic data types
```rust enum Maybe
{ Some(A), None } ``` ```rust let x = Some(42); let is_defined = match x { None => false, Some(_) => true, }; assert!(is_defined == true); ``` --- # Functional Rust ##
Algebraic data types
```rust #[derive(Clone, Debug)] pub enum Nat { Zero, Succ(Nat), } ``` .red[error: recursive type `nat::Nat` has infinite size] --- # Functional Rust ##
Algebraic data types
```rust #[derive(Clone, Debug)] pub enum Nat { Zero, Succ(Box
), } ``` ```rust pub fn add(this: &mut Nat, that: &Nat) -> () { match that { &Zero => { }, &Succ(ref n) => { *this = Succ(Box::new(this.clone())); add(this, n.as_ref()); } } } ``` ??? Mutation have to be used here: >> as we are working with references, there is no way to create new values "generate" new ownership But we can encapsulate that in a immutable API ... By implementing the `Add` trait we can operators for our custom types. --- # Functional Rust ##
Type classes
```rust pub trait Semigroup { fn append(&self, b: &Self) -> Self; } pub trait Monoid : Semigroup { fn empty() -> Self; } ``` --- # Functional Rust ##
Type classes
```rust pub trait Semigroup { fn append(&self, b: &Self) -> Self; } pub trait Monoid : Semigroup { fn empty() -> Self; } ``` ```rust impl Semigroup for u8 { fn append(&self, b: &u8) -> u8 { self + b } } impl Monoid for u8 { fn empty() -> u8 { 0 } } ``` ```rust use {Monoid, Semigroup}; let x = empty::
().append(&42); assert!(42 == x); ``` ??? Inheritance for type classes, but not for data types (like in Haskell) --- # Functional Rust ##
Associated Types
```rust trait Graph
{ fn edges(&self, &N) -> Vec
; } ``` ```rust fn distance
N, E>(graph: &G, start: &N, end: &N) -> u32 { ... } ``` ??? Associated types can provide functionality similar to Type Familiy (Haskell). Here, We have to define the `E` type parameter that we don't use in our distance function... --- # Functional Rust ##
Associated Types
```rust trait Graph { type N; type E; fn edges(&self, &N) -> Vec
; } ``` ```rust impl Graph for Foo { type N = u32; type E = u32; } ``` ```rust fn distance
(graph: &G, start: &G::N, end: &G::N) -> u32 { ... } ``` --- # DysFunctional Rust --- # DysFunctional Rust ##
Higher-kinded types
```rust trait Functor
> { fn map
(&self: F
, f: fn(A) -> B) -> F
; } ``` ??? Ideally we would like to define Functor with a trait like that... ... but that won't compile: no higher-kinded types. --- # DysFunctional Rust ##
Higher-kinded types
```rust pub trait Map
where F: Fn(&A) -> B { type Out; fn map(&self, f: F) -> Self::Out; } ``` ```rust pub trait FlatMap
where F: Fn(&A) -> Self::Out { type Out; fn flat_map(&self, f: F) -> Self::Out; } ``` ??? There exists some experiment to encode typeclasses with alternative encoding, but they are not practical. --- # DysFunctional Rust ##
Higher-kinded types
.center[### RFC 324 - [https://github.com/rust-lang/rfcs/issues/324](https://github.com/rust-lang/rfcs/issues/324)] --- # DysFunctional Rust ##
Effect tracking
```rust impl Monoid for u8 { fn empty() -> u8 { // Heh println!("Launch rocket!"); 0 } } ```
??? Having HKT would enable the design of effect librairies --- # DysFunctional Rust ##
Variance rules
```rust struct Foo<'a, 'b, A: 'a, B: 'b, C, D, E, F, G, H> { a: &'a A, // variant over 'a and A b: &'b mut B, // variant over 'b and invariant over B c: *const C, // variant over C d: *mut D, // invariant over D e: Vec
, // variant over E f: Cell
, // invariant over F g: G, // variant over G h1: H, // would also be variant over H except... h2: Cell
, // invariant over H, because invariance wins } ``` [https://doc.rust-lang.org/beta/nomicon/subtyping.html](https://doc.rust-lang.org/beta/nomicon/subtyping.html) --- # Practical Rust --- # Practical Rust ##
Error handling
```rust enum Result
{ Ok(T), Err(E), } ``` --- # Practical Rust ##
Error handling
```rust enum Result
{ Ok(T), Err(E), } ``` ```rust type User = (); enum Error { NotFound, Unauthorized, } fn login(user_id: u8) -> Result
{ Ok(()) } fn get_name(user: User) -> Result
{ Ok(String::from("Alice")) } ``` --- # Practical Rust ##
Error handling
```rust fn print_name(user_id: u8) -> Result<(), Error> { login(user_id) .and_then(|u| get_name(u)) // == Bind/FlatMap .map(|name| { println!("{}", name); () }) } ``` ??? Using `and_then` when can combine results (this is the monadic bind). --- # Practical Rust ##
Error handling
```rust fn print_name(user_id: u8) -> Result<(), Error> { let user = login(user_id)?; let name = get_name(user)?; println!("{}", name); Ok(()) } ```
.center[This is a specialized form of **idiom brackets**!]
Applicative Programming with Effects (Conor McBride) [http://www.staff.city.ac.uk/~ross/papers/Applicative.html](http://www.staff.city.ac.uk/~ross/papers/Applicative.html) ??? This syntax called "Idiom brackets" is implemented in Idris with the `!` symbol. In Rust, it works only on `Result`! No `Applicative` defined ... --- # Practical Rust ##
Lifetimes
```rust fn foo(s: &i8); // implicit fn foo<'a>(s: &'a i8); // explicit ``` --- # Practical Rust ##
Lifetimes
```rust struct Foo { value: i8 } struct Bar { foo: Foo } fn choose(a: &Bar, b: &Bar) -> &Foo { if a.foo.value > 2 { &a.foo } else { &b.foo } } ``` .red[error: missing lifetime specifier] ```rust let ref b1 = Bar { foo: Foo { value: 2 } }; let ref b2 = Bar { foo: Foo { value: 4 } }; choose(b1, b2); ``` ??? Elided means there is an implicit lifetime parameter being infered. Lifetime parameter provide a way to guide the borrow checker to track ownerships. Compiler says: >> help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b` --- # Practical Rust ##
Lifetimes
```rust struct Foo { value: i8 } struct Bar { foo: Foo } fn choose<'a>(a: &'a Bar, b: &'a Bar) -> &'a Foo { if a.foo.value > 2 { &a.foo } else { &b.foo } } ``` ```rust let ref b1 = Bar { foo: Foo { value: 2 } }; let ref b1 = Bar { foo: Foo { value: 4 } }; choose(b1, b2); ``` --- # Practical Rust ##
Lifetimes
```rust fn first<'a>(a: &Bar, b: &'a Bar) -> &'a Foo { &a.foo } ``` .red[error: lifetime `'a` required] --- # Practical Rust ##
Lifetimes
```rust fn first<'a>(a: &'a Bar, b: &Bar) -> &'a Foo { &a.foo } ``` --- # Practical Rust ##
Mutation tracking
```rust fn f(file: File) -> Result
{ let mut s = String::new(); file.read_to_string(&mut s)?; Ok(s) } ``` .red[error: cannot borrow immutable argument `file` as mutable] ```rust fn run() -> Result<(), Error> { let path = Path::new("/tmp/foo.txt"); let file = File::open(path)?; f(file)?; Ok(()) } ``` --- # Practical Rust ##
Mutation tracking
```rust fn f(file: &mut File) -> Result
{ let mut s = String::new(); file.read_to_string(&mut s)?; Ok(s) } ```
```rust fn run() -> Result<(), Error> { let path = Path::new("/tmp/foo.txt"); let mut file = File::open(path)?; f(&mut file)?; Ok(()) } ``` --- # Practical Rust ##
Affine types
```rust fn run() -> Result<(), Error> { let path = Path::new("/tmp/foo.txt"); let mut file = File::open(path)?; drop(file); f(&mut file)?; Ok(()) } ``` .red[error: use of moved value: `file`] ??? Linear types: useed exactly once. Affine types: use at most one. There can be only one owner (or "use") of the value. Passing by reference is not borrowing ownership, but by value does! --- # Practical Rust ##
Affine types
```rust fn run() -> Result<(), Error> { let path = Path::new("/tmp/foo.txt"); let mut file = File::open(path)?; f(&mut file)?; drop(file); Ok(()) } ``` --- # Practical Rust ##
Wrapper types
- ### `Box
` to hand out borrowed values - ### `Arc
` to share a value between threads - ### `Rc
` to share ownership using reference counting ??? Box introduce heap allocation. Arc and Rc does introduce a runtime cost for managment. --- # Practical Rust ##
Testing
```rust #[cfg(test)] mod tests { #[test] fn it_works() { assert!(42 = f(2)); } } ``` ``` running 1 test test it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured ``` --- # Practical Rust ##
Benchmarking
```rust #[cfg(test)] mod tests { #[bench] fn bench_f(b: &mut Bencher) { b.iter(|| f(2)); } } ``` ``` running 1 tests test tests::bench_f ... bench: 1 ns/iter (+/- 0) test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured ``` --- # Practical Rust ##
... More ...
- ### Automatic type classes (Trait) derivation - ### Concurrency with Channels (ala Haskell) - ### Hygienic macro system (supporting recursive and variadic macros) - ### Parallel Iterators with [Rayon](https://github.com/rayon-rs/rayon) - ... --- # Thanks! - Start learning **Rust** now with [rustbyexample.com](https://rustbyexample.com/) ## References - [The Rust Programming Language](https://doc.rust-lang.org/book/) - The Book - [Interview on Rust](https://www.infoq.com/news/2012/08/Interview-Rust) - with Graydon Hoare - [Rust for Functional Programmers](https://arxiv.org/abs/1407.5670) - by Raphel Poss (`kena`) - [Fearless Concurrency in Firefox Quantum](https://blog.rust-lang.org/2017/11/14/Fearless-Concurrency-In-Firefox-Quantum.html) - a success story from Mozilla ??? Rust is a Functional Language even if not exactly advertised as such ... (... the main reason is that "FP" might scare C/C++ programmer) In a nutshell, Rust keeps the C abstract model but innovate at language level. I hope this presentation gave you motivation to learn this nice language... ... and maybe finally tackle those problems you kept aside because of lack of proper tool for systems programming!