Why Rust?
Presented @ Codermine
by Giuseppe Arici & Gianluca Fioletti
https://codermine.github.io/whyrust/
Rust has been voted "Most Loved Programming Language" every year in the past five Stack Overflow developer surveys (2016, 2017, 2018, 2019, 2020). 🏆
Welcome
Rust
Hello, [c]rustaceans, I am Ferris the crab, unofficial mascot for Rust.
The Rust Buzz
- 🥇 By Mozilla For System Programming
- 🏅 Community Driven and Open Source
- 🎖 Performance, Reliability, Productivity
The Rust Bits
- ⚽️ Compiled Language (LLVM Backend)
- 🏀 Strong And Static Typing
- ⚾️ Imperative, But With Functional Aspects
- 🎾 No Garbage Collection Or Runtime
- 🏈 Elaborate Type System (No Null)
- 🏉 No Type Inheritance
- 🏓 Powerful Pattern Matching
- 🏸 Fearless Concurrency
History
The History of Rust (video by Steve Klabnik)
- 🟤 Graydon Hoare / Personal Years (2006-2010)
- 🟠 Graydon Hoare / Mozilla Years (2010-2012)
- 🟡 The Rust Team / Type System Years (2012-2014)
- 🟢 The Rust Team / Release 1.0 Years (2015-2017)
- 🔵 The Rust Team / Production Years (2018-2020)
- 🟣 The Rust Foundation / Future Years (2021→)
The Curious Case of Graydon Hoare
I wonder, why Graydon Hoare, the author of Rust, stopped contributing into it and switched to Swift?
I burnt out; ran out of emotional energy to be effective in my role as technical lead for the project mid way through 2013, so I took a break, switched off the Rust team, took a year to work on less-time-sensitive projects inside Mozilla, eventually quit Mozilla and worked for a completely unrelated payment network [...] for another year and a half, then finally in early 2016 got a call from someone at Apple saying they were looking for some folks to help with Swift (in a non-leadership position, which I prefer).
Governance
Each major decision in Rust starts as a Request for Comments (RFC). Everyone is invited to discuss the proposal, to work toward a shared understanding of the tradeoffs. Though sometimes arduous, this community deliberation is Rust’s secret sauce for quality.
Teams
The Core Team
www.rust-lang.org/governance/teams/core
The Foundation
Laying the foundation for Rust's future
The Rust Core Team and Mozilla are happy to announce plans to create a Rust foundation. The Rust Core Team's goal is to have the first iteration of the foundation up and running by the end of the year.
Setup
* not required for this presentation!
www.rust-lang.org/tools/install
On macOS, Linux and Unix
Use rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
On Windows
Download and run rustup-init.exe
Editors Setup
* not required for this presentation!
Online Setup
* recommended for this presentation
This presentation uses mdBook to provide runnable code playgrounds.
fn main() { println!("it works!"); }
Playground
If you prefer, you can use the Rust Playground directly.
Summary
- Welcome [@ Giuseppe: 5']
- Hello Rust [@ Gianluca: 30']
- More Of Rust [@ Giuseppe: 10']
- Rust Values [@ Giuseppe: 5']
- Rust Ecosystem [@ Giuseppe: 10']
⚠️ WARNING: these slides are meant for a one day course, so during the presentation we will skip some parts. They will remain online for those who want to deepen.
Hello Rust
- Main
- Variables Bindings
- Primitive Types
- Functions
- Ownership System
- Structs
- Expressions
- Traits
- Strings Slices
- Closures
- Multithreading
- Async Await
Main
-
main
function entry point -
println!
macro
Main Example
fn main() { println!("Hello, world!"); }
Variables Bindings
-
let var_name: Type = val;
-
let PATTERN = EXPRESSION;
-
Immutability by default
-
Type inference
Variables Bindings Example
fn main() { // Immutable variable let my_var: i32 = 5; println!("my_var is {}.", my_var); // Uncomment to see the error. // my_var = 6; println!("my_var is {}.", my_var); // Mutable variable let mut x = 5; println!("x is {}.", x); x = 10; println!("x is {}.", x); }
Shadowing
fn main() { let spaces = " "; { println!("spaces is '{}'.", spaces); //Remove "let" to see error. let spaces = spaces.len(); println!("spaces is {}.", spaces); } println!("spaces is {}.", spaces); }
Constants
// Note the underscore to make numbers more readable const MAX_POINTS: u32 = 100_000; fn main() { println!("x is {}.", MAX_POINTS); // Cannot be shadowed: uncomment to see // let MAX_POINTS = 10; }
- Annotation type required
Primitive Types
-
Scalar
-
Compound
Scalar Types
-
Integer
- signed:
i8, i16, i32, i64, isize
(arch) - unsigned:
u8, u16, u32, u64, usize
(arch)
- signed:
-
Floating-point:
f32, f64
-
Boolean
-
Character: unicode scalar value of 4 bytes size
Scalar Types Example
fn main() { // Inferred as i32 let a = 5; // Unsigned let b: u8 = 10; //Boolean let c = true; let d: bool = false; //Character let e = '\u{1F980}'; println!("e is: {}", e); let f: char = e; }
Compound Types
-
Tuple
- Fixed size
- Elements of different types
-
Array
- Fixed size
- Same types
Compound Types Example
fn main() { // Tuple let x: (i32, f64, u8) = (500, 6.4, 1); let five_hundred = x.0; println!("five_hundred is {}.", five_hundred); let six_point_four = x.1; println!("six_point_four is {}.", six_point_four); //Array let week_days: [&'static str; 7] = [ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" ]; // Note the question mark! println!("week_days are {:?}.", week_days); // Invalid array element access // Uncomment to see the error // let idx: usize = "7".parse().unwrap(); // println!("Eighth day is {}.", week_days[idx]); // Initialization let y = [3; 5]; println!("y is {:?}.", y); }
Functions
-
Mandatory parameter types
-
Mandatory return type
-
Optional return keyword
-
No overloading
-
Function pointer
-
Generics support
Functions Example 1
fn main() { // with arguments say_hello("rust"); // with return value let v = year_of_birth(); println!("Born in {}", v); let v = birthday(); println!("Born on {}", v); let (m, d, y) = birthday_as_tuple(); println!("Born on {} {}, {}", m, d, y); } fn say_hello(s: &str) { println!("Hello, {}!", s); } fn year_of_birth() -> &'static i32 { &2015 } fn birthday() -> &'static str { "May 15, 2015" } fn birthday_as_tuple() -> (&'static str, u8, u16) { ("May", 15, 2015) }
Functions Example 2
use std::fmt::Display; fn main() { tell_me(year_of_birth); tell_me(birthday); } fn tell_me<T: ?Sized + Display>(f: fn() -> &'static T) { println!("{}", f()); } fn year_of_birth() -> &'static i32 { &2015 } fn birthday() -> &'static str { "May 15, 2015" }
Ownership System
-
The Stack and the Heap
-
Ownership Rules
-
Borrowing
The Stack and the Heap
-
Stack
- Pushing and popping fixed size of data
- Known at compile time
- LIFO
-
Heap
- Allocating dynamic data at runtime
Ownership Rules
-
The variable is the owner of the value
-
Only one owner at a time
- Value dropped when owner goes out of scope (RAII)
Ownership Rules Example
fn main() { let ref a; { // Value allocated on heap let mut s = String::from("Hello"); s.push_str(", world!"); println!("s is {}", s); a = &s; } // s is dropped here // Uncomment to see the error // println!("a is {}", a); { let x = 1; println!("x is {}", x); } // x is dropped here // Uncomment to see the error // println!("x is {}", x); }
Move
fn main() { let s1 = String::from("hello"); let s2 = s1; println!("s2 is {}", s2); // Uncomment to see the error // println!("s1 is {}", s1); // s1 is no more valid ^^ } // Only heap of s2 will be dropped
- No double free error
Copy
fn main() { let s1 = 1; let s2 = s1; println!("s1 is {}", s1); println!("s2 is {}", s2); //Explained later #[derive(Debug, Clone, Copy)] struct Rectangle { width: u32, height: u32, }; let rect1 = Rectangle { width: 2, height: 3, }; let rect2 = rect1; println!("rect1 is {:?}", rect1); println!("rect2 is {:?}", rect2); }
-
Copy trait
- Memory allocated on stack
-
Type implementing Drop trait cannot marked as Copy
-
Examples of copy:
- Primitive types
- Group of copy
Clone
fn main(){ let s1 = String::from("hello"); let s2 = s1.clone(); println!("s1 is {}", s1); println!("s1 is {}", s2); }
- No automatic deep copy
Borrowing
fn main() { let s1 = String::from("hello"); let s = &s1; let len = calculate_lenght(s); println!("len is {}", len); } fn calculate_lenght(s: &String) -> usize { s.len() }
Immutable and Mutable References
//Remove every mut to see error fn main() { let mut s = String::from("hello"); change(&mut s); println!("s is {}", s); } fn change(s: &mut String) { s.push_str(", world!") }
Dangling References
fn main() { //let dangle = dangle(); let no_dangle = no_dangle(); println!("no_dangle is {}", no_dangle) } fn no_dangle() -> String { let s = String::from("hello"); s } // Uncomment to see error // fn dangle() -> &String { // let s = String::from("hello"); // &s // }
Rules of References
-
One mutable reference or any number of immutable references
-
References must always be valid
Lifetime
-
Syntax:
'a
-
Lifetime in functions
-
Lifetime in structs
Lifetimes in Functions
fn main() { let a = String::from("a"); { let b = String::from("bb"); let s1 = longest_1(a.as_str(), b.as_str()); println!("s1 is {}", s1); let s2 = longest_2(a.as_str(), b.as_str()); println!("s2 is {}", s2); } //println!("s1 is {}", s); //println!("s2 is {}", s); } fn longest_1<'a:'b,'b>(x: &'a str, y: &'b str) -> &'b str { if x.len() > y.len() { x } else { y } } fn longest_2<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
- Lifetimes annotations describe the relationships of the lifetimes of multiple references to each other
Lifetime in Structs
#[derive(Debug)] struct MyStruct<'a> { v: &'a i32, } fn main() { let m; { let x = 1; m = MyStruct { v: &x }; println!("m is {:?}", m); } //Uncommment to see the error //println!("m is {:?}", m); }
- MyStruct instance can't outlive the reference it holds
Lifetime Elision
-
Each reference parameter get its own lifetime
-
If there is exactly one input lifetime parameter that lifetime is assigned to all output lifetime parameters
-
It there are multiple lifetime parameters but one of theme is &self or &mut self the lifetime of self is assigned to all output parameters.
Lifetime Elision Example
fn main() { println!("{}", foo("hello")); println!("{}", foo_2("hello")); } // First rule // fn foo<'a,'b>(a: &'a str) -> &'b str { // Second rule // fn foo<'a>(a: &'a str) -> &'a str { fn foo(a: &str) -> &str { a } // Manual settings: 'a must outlive 'b fn foo_2<'a: 'b, 'b>(a: &'a str) -> &'b str { a }
Structs
-
Structs
-
Tuple structs
Structs
- custom data type
- init shorthand
- struct update syntax
Structs Init Shorthand Example
#[derive(Debug)] struct User { username: String, email: String, active: bool, } fn main() { let user_1 = User { username: String::from("Ferris"), email: String::from("ferris@codermine.com"), active: true, }; println!("{:?}", user_1); let email = String::from("g.fioletti@codermine.com"); //Init shorthand let user_2 = User { username: String::from("Gianluca"), email, active: true, }; println!("{:?}", user_2); }
Structs Update Syntax Example
#[derive(Debug)] struct User { username: String, email: String, active: bool, } fn main() { let user_1 = User { username: String::from("Ferris"), email: String::from("ferris@codermine.com"), active: true, }; println!("{:?}", user_1); //Struct update syntax let user_2 = User { username: String::from("rust"), ..user_1 }; println!("{:?}", user_2); }
Tuple Structs
-
Structs without named fields
-
Newtype Pattern
-
Type safety
Tuple Structs Example
use std::ops::Add; #[derive(Debug)] struct Rectangle(u32, u32); #[derive(Debug)] struct Meters(u32); impl Add for Meters { type Output = u32; fn add(self, arg: Self) -> Self::Output { self.0 * arg.0 } } fn main() { // Basic usage let rect = Rectangle(4, 8); println!("{:?}", rect); println!("Area is {:?}", area(rect)); // Type safety let x = Meters(2); let y = Meters(3); let lenght = x + y; println!("length is {:?}", lenght); } fn area(rect: Rectangle) -> u32 { rect.0 * rect.1 }
Impl & Methods
struct Rectangle { width: u32, height: u32, } impl Rectangle { // Syntatic sugar for fn area(self: Self) -> u32 { fn area(self) -> u32 { self.width * self.height } } fn main() { let rect = Rectangle { width: 2, height: 3, }; assert_eq!(6, rect.area()); }
Expressions
-
Rust is expression based
-
Statements perform actions but do not return a value
-
Expressions evaluate to a resulting value
Expressions Example
fn main() { // "2.0" is an expression let a = 2.0; // "let b = 3.0;" is a statement let b = 3.0; //"b" ^ is a pattern println!("a times b is {}", multiply(a, b)); //Statement let a = { //Expression if 4 % 2 == 0 { //Expression "even" } else { //Expression "odd" } }; assert_eq!(a, "even"); } fn multiply(a: f64, b: f64) -> f64 { //Expression a * b }
Traits
-
Similar to interfaces in other languages
-
Group of method signatures
-
Default implementations
-
Orphan rule: you can't implement external trait on external types
-
Generic support
-
Traits as parameters
-
Trait bound
-
Blanket implementation
Traits Example 1
use std::fmt::Display; trait Double { fn double(self) -> String; } impl Double for &str { fn double(self) -> String { let mut owned: String = self.to_owned().to_string(); owned.push_str(self); owned } } // Trait as parameter fn print_double(v: impl Double+Display) { println!("Here is a double {}: ", v.double()); } fn main() { println!("{}", "Ciao ".double()); print_double("Ciao "); }
Traits Example 2
/* impl<T:Display> ToString for T { // --snip-- } */ fn main() { let s = 5.to_string(); assert_eq!("5", s); }
- Blanket implementation
Strings & Slices
-
String:
let s = String::from("hello");
- use memory heap
-
String slices are reference to part of a String
- Syntax:
let slice = &s[0..3]
; - String literals:
"hello"
are string slices- the type is
&'static str
- the type is
- Syntax:
Closures
-
Optional parameter types
-
Optional return type
-
Memoization and lazy evaluation
-
Capture environment
Closures Example
fn main() { let square = |num| num*num; let num = 3; println!("The square of {} is {}", num, square(num)); }
- Closures implement FnOnce or FnMut or Fn traits;
Multithreading
-
1:1 threading model: no green threads in standard crate
- M:N model (green thrads) may be provided by other libraries
-
Message Passing model to transfer data.
- Rust slogan:
Do not communicate by sharing memory; instead, share memory by communicating.
-
Shared-State Concurrency
Message Passing
use std::sync::mpsc; use std::thread; use std::time::Duration; fn main() { let (tx, rx) = mpsc::channel(); let tx1 = mpsc::Sender::clone(&tx); thread::spawn(move || { let vs = vec![ String::from("a1"), String::from("a2"), String::from("a3"), ]; for v in vs { tx1.send(v).unwrap(); thread::sleep(Duration::from_secs(1)); } }); thread::spawn(move || { let vs = vec![ String::from("b1"), String::from("b2"), String::from("b3"), ]; for v in vs { tx.send(v).unwrap(); thread::sleep(Duration::from_secs(1)); } }); //for received in rx.iter().skip(1) { for received in rx { println!("Got: {}", received); } }
Shared State
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let shared_value = Arc::new(Mutex::new(String::new())); let mut handles = vec![]; for i in 0..10 { let shared_value = Arc::clone(&shared_value); let handle = thread::spawn(move || { let mut s = shared_value.lock().unwrap(); s.push_str(&i.to_string()); }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *shared_value.lock().unwrap()); }
Async Await
-
recently added
async
await
support in language -
Future trait in standard library
Async Await Example
use futures::executor::block_on; async fn hello_world() { println!("hello, world!"); } async fn async_main(){ let future = hello_world(); // Nothing is printed let a = future.await; } fn main() { block_on(async_main()); }
More Of Rust
- Pattern Matching
- Enumerations
- Generic Types
- Option
- Result
- Error Handling
- Modules
- Macro Rules
- Attributes
- Unsafe
- Testing
Pattern Matching
-
Patterns are a special syntax in Rust for matching against the structure of types, both complex and simple
-
Rust provides pattern matching via the
match
keyword, which can be used like a C switch.
Pattern Matching Example
fn main() { let number = 13; // TODO ^ Try different values for `number` println!("Tell me about {}", number); match number { // Match a single value 1 => println!("One!"), // Match several values 2 | 3 | 5 | 7 | 11 => println!("This is a prime"), // Match an inclusive range 13..=19 => println!("A teen"), // Handle the rest of cases _ => println!("Ain't special"), } let boolean = true; // Match is an expression too let binary = match boolean { // The arms of a match must cover all the possible values false => 0, true => 1, // TODO ^ Try commenting out one of these arms }; println!("{} -> {}", boolean, binary); }
Destructuring
-
Destructuring is done primarily through the
let
andmatch
statements -
A
let
pulls the variables out into the current scope, whereasmatch
introduces a new scope
fn foo(pair: (int, int)) { let (x, y) = pair; // we can now use x and y anywhere in foo match pair { (x, y) => { // x and y can only be used in this scope } } }
Destructuring Example
fn main() { struct Foo { x: (u32, u32), y: u32, } // Try changing the values in the struct to see what happens let foo = Foo { x: (1, 2), y: 3 }; match foo { Foo { x: (1, b), y } => println!("First of x is 1, b = {}, y = {} ", b, y), // you can destructure structs and rename the variables, // the order is not important Foo { y: 2, x: i } => println!("y is 2, i = {:?}", i), // and you can also ignore some variables: Foo { y, .. } => println!("y = {}, we don't care about x", y), // this will give an error: pattern does not mention field `x` //Foo { y } => println!("y = {}", y), } }
Guards
- A
match
guard can be added to filter the arm
(x, y) if x == y => println!("These are twins"),
Guards Example
fn main() { let pair = (2, -2); // TODO ^ Try different values for `pair` println!("Tell me about {:?}", pair); match pair { (x, y) if x == y => println!("These are twins"), // The ^ `if condition` part is a guard (x, y) if x + y == 0 => println!("Antimatter, kaboom!"), (x, _) if x % 2 == 1 => println!("The first one is odd"), _ => println!("No correlation..."), } }
Binding
-
Indirectly accessing a variable makes it impossible to use that variable without re-binding
-
match
provides the@
sigil for binding values to names
Binding Example
// A function `age` which returns a `u32`. fn age() -> u32 { 15 } fn main() { println!("Tell me what type of person you are"); match age() { 0 => println!("I haven't celebrated my first birthday yet"), // Could `match` 1 ..= 12 directly but then what age // would the child be? Instead, bind to `n` for the // sequence of 1 ..= 12. Now the age can be reported. n @ 1..=12 => println!("I'm a child of age {:?}", n), n @ 13..=19 => println!("I'm a teen of age {:?}", n), // Nothing bound. Return the result. n => println!("I'm an old person of age {:?}", n), } }
Binding Destructuring Example
- You can also use binding to "destructure"
enum
variants, such asOption
:
fn some_number() -> Option<u32> { Some(42) } fn main() { match some_number() { // Got `Some` variant, match if its value, bound to `n`, // is equal to 42. Some(n @ 42) => println!("The Answer: {}!", n), // Match any other number. Some(n) => println!("Not interesting... {}", n), // Match anything else (`None` variant). _ => (), } }
Enumerations
-
Enumerations allow you to create a new type that can have a value of several tagged elements using the enum keyword
-
match
helps ensure exhaustive handling of all possible enum values making it a powerful tool in ensuring quality code
Enumerations Example
#![allow(dead_code)] // this line prevents compiler warnings enum Species { Crab, Octopus, Fish, Clam, } struct SeaCreature { species: Species, name: String, arms: i32, legs: i32, weapon: String, } fn main() { let ferris = SeaCreature { species: Species::Crab, name: String::from("Ferris"), arms: 2, legs: 4, weapon: String::from("claw"), }; match ferris.species { Species::Crab => println!("{} is a crab", ferris.name), Species::Octopus => println!("{} is a octopus", ferris.name), Species::Fish => println!("{} is a fish", ferris.name), Species::Clam => println!("{} is a clam", ferris.name), } }
Enumerations With Data
-
Enumerations elements can also have one or more data types
-
Rust's enum is something also known as a tagged union
-
When an enum is pattern matched using match, you can bind a variable name to each value
-
An enum data value will have a memory size equal to its largest element
Enumerations With Data Example
#![allow(dead_code)] // this line prevents compiler warnings enum Species { Crab, Octopus, Fish, Clam, } enum PoisonType { Acidic, Painful, Lethal, } enum Size { Big, Small, } enum Weapon { Claw(i32, Size), Poison(PoisonType), None, } struct SeaCreature { species: Species, name: String, arms: i32, legs: i32, weapon: Weapon, } fn main() { // SeaCreature's data is on stack let ferris = SeaCreature { // String struct is also on stack, // but holds a reference to data on heap species: Species::Crab, name: String::from("Ferris"), arms: 2, legs: 4, weapon: Weapon::Claw(2, Size::Small), }; match ferris.species { Species::Crab => match ferris.weapon { Weapon::Claw(num_claws, size) => { let size_description = match size { Size::Big => "big", Size::Small => "small", }; println!( "ferris is a crab with {} {} claws", num_claws, size_description ) } _ => println!("ferris is a crab with some other weapon"), }, _ => println!("ferris is some other animal"), } }
Enumerations Destructuring
- The
match
statement is used when the structure being destructured can have difference variants such as an enumerations
Enumerations Destructuring Example
// `allow` required to silence warnings because only // one variant is used. #[allow(dead_code)] enum Color { // These 3 are specified solely by their name. Red, Blue, Green, // These likewise tie `u32` tuples to different names: color models. RGB(u32, u32, u32), HSV(u32, u32, u32), HSL(u32, u32, u32), CMY(u32, u32, u32), CMYK(u32, u32, u32, u32), } fn main() { let color = Color::RGB(122, 17, 40); // TODO ^ Try different variants for `color` println!("What color is it?"); // An `enum` can be destructured using a `match`. match color { Color::Red => println!("The color is Red!"), Color::Blue => println!("The color is Blue!"), Color::Green => println!("The color is Green!"), Color::RGB(r, g, b) => println!("Red: {}, green: {}, and blue: {}!", r, g, b), Color::HSV(h, s, v) => println!("Hue: {}, saturation: {}, value: {}!", h, s, v), Color::HSL(h, s, l) => println!("Hue: {}, saturation: {}, lightness: {}!", h, s, l), Color::CMY(c, m, y) => println!("Cyan: {}, magenta: {}, yellow: {}!", c, m, y), Color::CMYK(c, m, y, k) => println!( "Cyan: {}, magenta: {}, yellow: {}, key (black): {}!", c, m, y, k ), // Don't need another arm because all variants have been examined } }
Generic Types
-
Generic types allow us to partially define a struct or enum, enabling a compiler to create a fully defined version at compile-time based off our code usage
-
Rust generally can infer the final type by looking at our instantiation, but if it needs help you can always be explicit using the ::
operator, also known by the name turbofish
Generics Types Example
struct Val { val: f64, } struct GenVal<T> { gen_val: T, } // impl of Val impl Val { fn value(&self) -> &f64 { &self.val } } // impl of GenVal for a generic type `T` impl<T> GenVal<T> { fn value(&self) -> &T { &self.gen_val } } fn main() { let x = Val { val: 3.0 }; let y = GenVal { gen_val: 3i32 }; println!("{}, {}", x.value(), y.value()); }
Bounds
-
When working with generics, the type parameters often must use traits as bounds to stipulate what functionality a type implements
-
The following example uses the trait Display to print and so it requires T to be bound by Display; that is, T must implement Display
// Define a function `printer` that takes a generic type `T` which // must implement trait `Display`. fn printer<T: Display>(t: T) { println!("{}", t); }
Bounds Example
// A trait which implements the print marker: `{:?}`. use std::fmt::Debug; trait HasArea { fn area(&self) -> f64; } impl HasArea for Rectangle { fn area(&self) -> f64 { self.length * self.height } } #[derive(Debug)] struct Rectangle { length: f64, height: f64, } #[allow(dead_code)] struct Triangle { length: f64, height: f64, } // The generic `T` must implement `Debug`. Regardless // of the type, this will work properly. fn print_debug<T: Debug>(t: &T) { println!("{:?}", t); } // `T` must implement `HasArea`. Any type which meets // the bound can access `HasArea`'s function `area`. fn area<T: HasArea>(t: &T) -> f64 { t.area() } fn main() { let rectangle = Rectangle { length: 3.0, height: 4.0, }; let _triangle = Triangle { length: 3.0, height: 4.0, }; print_debug(&rectangle); println!("Area: {}", area(&rectangle)); //print_debug(&_triangle); //println!("Area: {}", area(&_triangle)); // ^ TODO: Try uncommenting these. // | Error: Does not implement either `Debug` or `HasArea`. }
Multiple Bounds
-
Multiple bounds can be applied with a
+
-
Like normal, different types are separated with
,
Multiple Bounds Example
use std::fmt::{Debug, Display}; fn compare_prints<T: Debug + Display>(t: &T) { println!("Debug: `{:?}`", t); println!("Display: `{}`", t); } fn compare_types<T: Debug, U: Debug>(t: &T, u: &U) { println!("t: `{:?}`", t); println!("u: `{:?}`", u); } fn main() { let string = "words"; let array = [1, 2, 3]; let vec = vec![1, 2, 3]; compare_prints(&string); //compare_prints(&array); // TODO ^ Try uncommenting this. compare_types(&array, &vec); }
Where Clause
-
A bound can also be expressed using a
where
clause immediately before the opening {, rather than at the type's first mention -
Additionally,
where
clauses can apply bounds to arbitrary types, rather than just to type parameters
impl <A: TraitB + TraitC, D: TraitE + TraitF> MyTrait<A, D> for YourType {} // Expressing bounds with a `where` clause impl <A, D> MyTrait<A, D> for YourType where A: TraitB + TraitC, D: TraitE + TraitF { }
Where Clause Example
-
A
where
clause is useful When using it is more expressive than using normal syntax -
The
impl
in this example cannot be directly expressed without awhere
clause
use std::fmt::Debug; trait PrintInOption { fn print_in_option(self); } // Because we would otherwise have to express this as `T: Debug` or // use another method of indirect approach, this requires a `where` clause: impl<T> PrintInOption for T where Option<T>: Debug, { // We want `Option<T>: Debug` as our bound because that is what's // being printed. Doing otherwise would be using the wrong bound. fn print_in_option(self) { println!("{:?}", Some(self)); } } fn main() { let vec = vec![1, 2, 3]; vec.print_in_option(); }
Option
-
Rust has a built in generic enum called
Option
that allows us to represent nullable values without using null -
This enum is so common, instances of the enum can be created anywhere with the enum variants
Some
andNone
#![allow(unused)] fn main() { enum Option<T> { None, Some(T), } }
Option Example
// A partially defined struct type struct BagOfHolding<T> { // Our parameter type T can be handed to others item: Option<T>, } fn main() { // Note: A bag for i32, holding nothing! We have to specify the type // because otherwise Rust would not know what type of bag it is. let i32_bag = BagOfHolding::<i32> { item: None }; if i32_bag.item.is_none() { println!("there's nothing in the bag!") } else { println!("there's something in the bag!") } let i32_bag = BagOfHolding::<i32> { item: Some(42) }; if i32_bag.item.is_some() { println!("there's something in the bag!") } else { println!("there's nothing in the bag!") } // match lets us deconstruct Option elegantly and ensure we handle all cases! match i32_bag.item { Some(v) => println!("found {} in bag!", v), None => println!("found nothing"), } }
If Let
- For some use cases, when matching enums,
match
is awkward
#![allow(unused)] fn main() { // Make `optional` of type `Option<i32>` let optional = Some(7); match optional { Some(i) => { println!("This is a really long string and `{:?}`", i); // ^ Needed 2 indentations just so we could destructure // `i` from the option. }, _ => {}, // ^ Required because `match` is exhaustive. Doesn't it seem // like wasted space? }; }
If Let Example
if let
is cleaner for this case and in addition allows various failure options to be specified
fn main() { // All have type `Option<i32>` let number = Some(7); let letter: Option<i32> = None; let emoticon: Option<i32> = None; // The `if let` construct reads: "if `let` destructures `number` into // `Some(i)`, evaluate the block (`{}`). if let Some(i) = number { println!("Matched {:?}!", i); } // If you need to specify a failure, use an else: if let Some(i) = letter { println!("Matched {:?}!", i); } else { // Destructure failed. Change to the failure case. println!("Didn't match a number. Let's go with a letter!"); } // Provide an altered failing condition. let i_like_letters = false; if let Some(i) = emoticon { println!("Matched {:?}!", i); // Destructure failed. Evaluate an `else if` condition to see if the // alternate failure branch should be taken: } else if i_like_letters { println!("Didn't match a number. Let's go with a letter!"); } else { // The condition evaluated false. This branch is the default: println!("I don't like letters. Let's go with an emoticon :)!"); } }
While Let
- Similar to if let,
while let
can make awkward match sequences more tolerable
#![allow(unused)] fn main() { // Make `optional` of type `Option<i32>` let mut optional = Some(0); // Repeatedly try this test. loop { match optional { // If `optional` destructures, evaluate the block. Some(i) => { if i > 9 { println!("Greater than 9, quit!"); optional = None; } else { println!("`i` is `{:?}`. Try again.", i); optional = Some(i + 1); } // ^ Requires 3 indentations! } // Quit the loop when the destructure fails: _ => { break; } // ^ Why should this be required? There must be a better way! } } }
While Let Example
- Using
while let
makes previous sequence much nicer
fn main() { // Make `optional` of type `Option<i32>` let mut optional = Some(0); // This reads: "while `let` destructures `optional` into // `Some(i)`, evaluate the block (`{}`). Else `break`. while let Some(i) = optional { if i > 9 { println!("Greater than 9, quit!"); optional = None; } else { println!("`i` is `{:?}`. Try again.", i); optional = Some(i + 1); } // ^ Less rightward drift and doesn't require // explicitly handling the failing case. } // ^ `if let` had additional optional `else`/`else if` // clauses. `while let` does not have these. }
Result
-
Rust has a built in generic enum called
Result
that allows us to return a value that has the possibility of failing. It is the idiomatic way in which the language does error handling. -
This enum is so common, instances of the enum can be created anywhere with the enum variants
Ok
andErr
.
#![allow(unused)] fn main() { enum Result<T, E> { Ok(T), Err(E), } }
Result Example
fn do_something_that_might_fail(i: i32) -> Result<f32, String> { if i == 42 { Ok(13.0) } else { Err(String::from("this is not the right number")) } } fn main() { let result = do_something_that_might_fail(12); // match lets us deconstruct Result elegantly and ensure we handle all cases! match result { Ok(v) => println!("found {}", v), Err(e) => println!("Error: {}", e), } }
Error Handling
-
main
has the capability of returning aResult
! -
Result
is so common that Rust has a powerful operator?
for working with them -
These two statements are equivalent
do_something_that_might_fail()?
match do_something_that_might_fail() { Ok(v) => v, Err(e) => return Err(e), }
Error Handling Example
fn do_something_that_might_fail(i: i32) -> Result<f32, String> { if i == 42 { Ok(13.0) } else { Err(String::from("this is not the right number")) } } // Main returns no value, but could return an error! fn main() -> Result<(), String> { // let result = do_something_that_might_fail(12); // match result { // Ok(v) => println!("found {}", v), // Err(_e) => { // // handle this error gracefully // // // return a new error from main that said what happened! // return Err(String::from("something went wrong in main!")); // } // } // Look at how much code we saved! let v = do_something_that_might_fail(42)?; println!("found {}", v); // Notice we use a unit value inside a Result Ok // to represent everything is fine Ok(()) }
Modules
-
Every Rust program or library is a crate
-
Every crate is made of a hierarchy of modules
-
Every crate has a root module
-
A module can hold global variables, functions, structs, traits or even other modules!
-
A program has a root module in a file called
main.rs
-
A library has a root module in a file called
lib.rs
Modules Use
-
Items in modules can be referenced with their full module path
std::f64::consts::PI
-
A simpler way is the
use
keyword; it allows us to specify particular items from modules we want to use throughout our code without a full path -
std
is the crate of the standard library of Rust which is full of useful data structures and functions for interacting with your operating system
Modules Use Example
use std::f64::consts::PI
allows us to just use the identifierPI
in my main function
use std::f64::consts::PI; fn main() { println!("Welcome to the playground!"); println!("I would love a slice of {}!", PI); }
- Multiple items can be referenced in a single module path as so
use std::f64::consts::{PI,TAU}
Modules Definition
-
In Rust there is not a 1 to 1 mapping of files to the module tree hierarchy
-
We must build the module tree explicitly by hand in our code
-
When we think of code, we usually imagine a hierarchy of files organized in directories
-
Rust lets you create modules closely related to your file structure
-
There are two ways in Rust to declare a module. For example, a module
foo
can be represented as- a file named
foo.rs
- a directory named
foo
with a filemod.rs
inside
- a file named
Modules Definition Example
- In order to establish a relationship between a module and its sub-module, you must write in the parent module
mod foo;
- The declaration above will look for a file named
foo.rs
orfoo/mod.rs
and will insert its contents inside a module namedfoo
under this scope
Modules Inline
-
A sub-module can be directly inlined within a module's code
-
One very common use for inline modules is creating unit tests
// This macro removes this inline module when Rust // is not in test mode. #[cfg(test)] mod tests { // Notice that we don't immediately get access to the // parent module. We must be explicit. use super::*; // ... tests go here ... }
Modules Referencing
-
Rust has several keywords you can use in your
use
path to quickly get ahold of the module you want:crate
the root module of your cratesuper
the parent module of your current moduleself
the current module
Modules Exporting
-
By default members of a module are not accessible from outside of the module (not even to its child modules!)
-
We make members of a module accessible using the
pub
keyword -
By default members of a crate are not accessible outside of the crate
-
We make members of a crate accessible by marking them as
pub
in the root module of your crate (lib.rs
ormain.rs
)
Modules Struct Visibility
- Just like functions, structs can declare what they want exposed outside of their module using
pub
// SeaCreature struct will be usable outside of our module pub struct SeaCreature { pub animal_type: String, pub name: String, pub arms: i32, pub legs: i32, // let's keep our weapon private weapon: String, }
Modules Prelude
-
We have access to
Option
orBox
everywhere without a use to import them because of the module prelude in the standard library -
Anything that is exported in
std::prelude::*
is automatically available to every part of Rust
Macro Rules
-
Rust provides a powerful macro system that allows metaprogramming
-
Macros look like functions, except that their name ends with a bang
!
-
Instead of generating a function call, macros are expanded into source code that gets compiled with the rest of the program
-
Unlike macros in C, Rust macros are expanded into abstract syntax trees, rather than string preprocessing
Macro Rules
- Macros are created using the
macro_rules!
macro.
// This is a simple macro named `say_hello`. macro_rules! say_hello { // `()` indicates that the macro takes no argument. () => { // The macro will expand into the contents of this block. println!("Hello!"); }; } fn main() { // This call will expand into `println!("Hello");` say_hello!() }
Attribrutes
- An attribute is metadata applied to some module, crate or item
#[attribute = "value"] #[attribute(key = "value")] #[attribute(value)] fn foo() {}
-
This metadata can be used to/for
- conditional compilation of code
- set crate name, version and type (binary or library)
- disable lints (warnings)
- enable compiler features (macros, glob imports, etc.)
- link to a foreign library
- mark functions as unit tests
- mark functions that will be part of a benchmark
- create custom decorator
Attributes Example
- Attribute macros are defined by a public function with the
proc_macro_attribute
attribute that has a signature of(TokenStream, TokenStream) -> TokenStream
// my-macro/src/lib.rs #[proc_macro_attribute] pub fn show_streams(attr: TokenStream, item: TokenStream) -> TokenStream { println!("attr: \"{}\"", attr.to_string()); println!("item: \"{}\"", item.to_string()); item }
// src/lib.rs extern crate my_macro; use my_macro::show_streams; // Example: Basic function #[show_streams] fn invoke1() {} // out: attr: "" // out: item: "fn invoke1() { }" // Example: Attribute with input #[show_streams(bar)] fn invoke2() {} // out: attr: "bar" // out: item: "fn invoke2() {}" // Example: Multiple tokens in the input #[show_streams(multiple => tokens)] fn invoke3() {} // out: attr: "multiple => tokens" // out: item: "fn invoke3() {}" // Example: #[show_streams { delimiters }] fn invoke4() {} // out: attr: "delimiters" // out: item: "fn invoke4() {}"
Unsafe
-
unsafe
annotations in Rust are used to bypass protections put in place by the compiler -
Specifically, there are four primary things that unsafe is used for
- dereferencing raw pointers
- calling functions or methods which are unsafe
- accessing or modifying static mutable variables
- implementing unsafe traits
Unsafe Example
use std::slice; fn main() { let raw_p: *const u32 = &10; // Dereferencing a raw pointer can only be done through an unsafe block. unsafe { assert!(*raw_p == 10); } let some_vector = vec![1, 2, 3, 4]; let pointer = some_vector.as_ptr(); let length = some_vector.len(); unsafe { // Calling Unsafe Functions let my_slice: &[u32] = slice::from_raw_parts(pointer, length); assert_eq!(some_vector.as_slice(), my_slice); } }
Testing
-
Rust is a programming language that cares a lot about correctness and it includes support for writing software tests within the language itself
-
Testing comes in three styles
- Unit testing
- Doc testing
- Integration testing
-
Also Rust has support for specifying additional dependencies for tests
# standard crate data is left out
[dev-dependencies]
pretty_assertions = "0.4.0"
Unit Testing
-
Unit tests go into a
tests
mod with the#[cfg(test)]
attribute -
Test functions are marked with the
#[test
attribute -
Tests fail when something in the test function panics
-
There are some helper macros:
assert!(expression)
panics if expression evaluates tofalse
assert_eq!(left, right)
andassert_ne!(left, right)
testing left and right expressions for equality and inequality respectively
Unit Testing Example
pub fn add(a: i32, b: i32) -> i32 { a + b } // This is a really bad adding function, its purpose is to fail in this // example. #[allow(dead_code)] fn bad_add(a: i32, b: i32) -> i32 { a - b } #[cfg(test)] mod tests { // Note this useful idiom: importing names from outer (for mod tests) scope. use super::*; #[test] fn test_add() { assert_eq!(add(1, 2), 3); } #[test] fn test_bad_add() { // This assert would fire and test will fail. // Please note, that private functions can be tested too! assert_eq!(bad_add(1, 2), 3); } }
Documentation Testing
-
The primary way of documenting a Rust project is through annotating the source code
-
Documentation comments are written in markdown and support code blocks in them
-
Rust takes care about correctness, so these code blocks are compiled and used as tests
Documentation Testing Example
/// First line is a short summary describing function. /// /// The next lines present detailed documentation. Code blocks start with /// triple backquotes and have implicit `fn main()` inside /// and `extern crate <cratename>`. Assume we're testing `doccomments` crate: /// /// ``` /// let result = doccomments::add(2, 3); /// assert_eq!(result, 5); /// ``` pub fn add(a: i32, b: i32) -> i32 { a + b } /// Usually doc comments may include sections "Examples", "Panics" and "Failures". /// /// The next function divides two numbers. /// /// # Examples /// /// ``` /// let result = doccomments::div(10, 2); /// assert_eq!(result, 5); /// ``` /// /// # Panics /// /// The function panics if the second argument is zero. /// /// ```rust,should_panic /// // panics on division by zero /// doccomments::div(10, 0); /// ``` pub fn div(a: i32, b: i32) -> i32 { if b == 0 { panic!("Divide-by-zero error"); } a / b }
- Tests can be run with
cargo test
$ cargo test
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Doc-tests doccomments
running 3 tests
test src/lib.rs - add (line 7) ... ok
test src/lib.rs - div (line 21) ... ok
test src/lib.rs - div (line 31) ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Rust Values
Quick Comparisons
Rust 💪 Python
- No GC Pauses
- No Interpreter Overhead
- Much Lower Memory Use
- Multi-Threading (No Gil)
- Comprehensive Static Typing
- Pattern Matching
- Many Fewer Runtime Crashes
Rust 💪 Java
- No GC Pauses
- No JVM Overhead
- Much Lower Memory Use
- No Null Pointers
- Zero-Cost Abstractions
- Pattern Matching
- Unified Build System
Rust 💪 C/C++
- No Segfaults
- No Buffer Overflows
- No Null Pointers
- No Data Races
- Stronger Type System
- Dependency Management
- Unified Build System
Rust 💪 Go
- No GC Pauses
- Lower Memory Use
- Nicer Error Handling
- No Null Pointers
- Safe Concurrency
- Stronger Type System
- Zero-Cost Abstractions
Final Thoughts
-
📕 Like C and C++, Rust's got predictable performance, with mature code optimizers (from LLVM) and a high performance ceiling
-
📗 Like C#, Java and other managed languages, Rust presents a comfortable, high-level standard library and set of language constructs with a lot of safety guarantees
-
📘 Like Haskell, Rust beats out every other language of noteworthy market share when it comes to how much you can get the compiler to verify for you at compile time
-
📙 Like Go, Rust links its own code statically by default, making deployment a breeze for pure Rust applications
-
📚 Rust is specifically designed so it can not only call C easily, but be called from C easily, making it almost unique in its viability for incrementally replacing bits of other programs with Rust wherever you need better performance or stricter compile-time guarantees
Rust Main Strengths 👍
-
🚀 Modern Language: nice and efficient generics, pattern matching, modern tooling
-
⛑ Safety by Construction: pointers checked at compile-time, thread-safety, no hidden states
-
🔬 Low Level Control: no gc or runtime, control allocation, can write low-level code
-
🏎 Blazingly Fast: can power performance-critical services, runs on embedded devices
-
🌈 Compatibility: zero-overhead ffi, great webassembly support, works with traditional tools
Rust Main Weaknesses 👎
-
🧗♂️ Learning Curve: the borrow checker is different, not the usual syntax we are used to
-
👩🎓 Young Ecosystem: not many maintainers, relatively small community (but growing quickly)
-
🔮 No Runtime Magic: no runtime codegen, no runtime reflection, no runtime debugging
-
🐌 Slow Compilation: improving, but still slow compilation, no pre-built libraries
-
🖼 Windows Support: full compiler / std support, limited library support
Rust Ecosystem
Tools
Cargo
Cargo – The official Rust package manager
Cargo has lots of really useful features to improve code quality and developer velocity:
- Project management
- Dependency management
- Awareness of compilation
- Awareness of unit tests
- Awareness of benchmarks
- Pluggable system
Cargo New Project
To create a new Rust project
# A binary
cargo new foo
# OR A library
cargo new --lib foo
After the above commands, you should see a file hierarchy like this
foo
├── Cargo.toml
└── src
└── main.rs
Cargo Toml
The Cargo.toml
is the config file for cargo
for the project
[package]
name = "foo"
version = "0.1.0"
authors = ["mark"]
[dependencies]
- The
name
field under[package]
determines the name of the project - The
version
field is a crate version number using Semantic Versioning - The
authors
field is a list of authors used when publishing the crate - The
[dependencies]
section lets you add dependencies for your project
Cargo Dependencies
[package]
name = "foo"
version = "0.1.0"
authors = ["mark"]
[dependencies]
clap = "2.27.1" # from crates.io
rand = { git = "https://github.com/rust-lang-nursery/rand" } # from online repo
bar = { path = "../bar" } # from a path in the local filesystem
Cargo Commands
To build our project we can execute cargo build
anywhere in the project directory (including subdirectories!).
We can also do cargo run
to build and run.
Notice that these commands will resolve all dependencies, download crates if needed, and build everything, including your crate.
Rustfmt
By default, Rustfmt uses a style which conforms to the Rust style guide that has been formalized through the style RFC process.
rustup update # udpate rustup and compiler
rustup component add rustfmt # install rustfmt
cargo fmt # run rustfmt
Rustfmt Config
Rustfmt is designed to be configurable. You can create a TOML file rustfmt.toml
or .rustfmt.toml
, place it in the project and it will apply the options in that file.
indent_style = "Block"
reorder_imports = false
use_field_init_shorthand = true
Rustfmt Example
Before fmt:
fn main() { let a=1; if a> 0 { println!("positive"); } else { println!("negative"); } }
After fmt:
fn main() { let a = 1; if a > 0 { println!("positive"); } else { println!("negative"); } }
Clippy
github.com/rust-lang/rust-clippy
There are over 400 lints included in this crate!
rustup update # udpate rustup and compiler
rustup component add clippy # install clippy
cargo clippy # run clippy
Clippy Config
Clippy is designed to be configurable. You can create a TOML file clippy.toml
or .clippy.toml
, place it in the project and it will apply the options in that file.
blacklisted-names = ["java", "c++", "php"]
cognitive-complexity-threshold = 30
Clippy Example
Example code:
fn main() { let pi = 3.14; let diameter = 1_f64; println!("Circumference: {}", diameter * pi); }
Apply clippy:
play (main #) $ cargo clippy
Checking play v0.1.0 (/Users/joseph/Temp/play)
error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
--> src/main.rs:2:14
|
2 | let pi = 3.14;
| ^^^^
|
= note: `#[deny(clippy::approx_constant)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
error: aborting due to previous error
error: could not compile `play`
To learn more, run the command again with --verbose.
Rustdoc
The standard Rust distribution ships with a tool called rustdoc
. Its job is to generate documentation for Rust projects.
Cargo also has integration with rustdoc to make it easier to generate docs.
cargo doc # build documentation in target/doc.
Rustdoc Example
Doc comments are very useful for big projects that require documentation. When running rustdoc, these are the comments that get compiled into documentation.
They are denoted by a ///, and support Markdown.
#![allow(unused)] fn main() { /// Shortcut method to quickly make a `GET` request. /// /// See also the methods on the [`reqwest::Response`](./struct.Response.html) /// type. /// /// **NOTE**: This function creates a new internal `Client` on each call, /// and so should not be used if making many requests. Create a /// [`Client`](./struct.Client.html) instead. /// /// # Examples /// /// ```rust /// # async fn run() -> Result<(), reqwest::Error> { /// let body = reqwest::get("https://www.rust-lang.org").await? /// .text().await?; /// # Ok(()) /// # } /// ``` /// /// # Errors /// /// This function fails if: /// /// - native TLS backend cannot be initialized /// - supplied `Url` cannot be parsed /// - there was an error while sending request /// - redirect limit was exhausted pub async fn get<T: IntoUrl>(url: T) -> crate::Result<Response> { Client::builder().build()?.get(url).send().await } }
Rustdoc Example Output
Libraries
doc.rust-lang.org/std is The Rust Standard Library
Rust tries to avoid stuffing everything into the standard library
Crates
crates.io is the Rust community’s package registry
Common Crates
-
📦 log – A library providing a lightweight logging facade
-
📦 rand – A library for random number generation
-
📦 serde – A library for serializing and deserializing data structures
-
📦 bytes – A library for working with bytes.
-
📦 regex – A library for parsing, compiling, and executing regular expressions
-
📦 chrono – A library for handling date and time
-
📦 clap – A library for parsing command line arguments and subcommands
-
📦 url – A library for parsing URL
-
📦 reqwest – An ergonomic, batteries-included HTTP Client
-
📦 diesel – A safe, extensible ORM and Query Builder for Rust
Awesome Rust
github.com/rust-unofficial/awesome-rust
Applications
Hundreds of companies around the world are using Rust in production today for fast, low-resource, cross-platform solutions. From startups to large corporations, from embedded devices to scalable web services, Rust is a great fit.
CLI
I have zero regrets with living in this code base. [...] This was sort of an added bonus for me: Using Rust to make CLI or console based tools. It is very good at compiling for different target systems.
mdBook
mdBook is a command line tool and Rust crate to create books using Markdown (as by the CommonMark specification) files. It's very similar to Gitbook but written in Rust.
This Source
This repository contains the source of the Why Rust? presentation.
System
Rust type system allows us to build modular, testable, composable units without sacrificing runtime performance. What’s been most surprising, though, is how Rust’s lifetime/borrow checking system allows us to avoid large classes of resource leaks. After 2 years, I really can’t imagine using any other language for the job.
Servo
Servo is an experimental browser engine
Servo’s mission is to provide an independent, modular, embeddable web engine, which allows developers to deliver content and applications using web standards. Servo is written in Rust, and shares code with Mozilla Firefox and the wider Rust ecosystem.
Redox
Redox is a Unix-like Operating System written in Rust
Redox is a Unix-like Operating System written in Rust, aiming to bring the innovations of Rust to a modern microkernel and full set of applications.
InfluxDB
Announcing InfluxDB IOx – The Future Core of InfluxDB Built with Rust and Arrow
Rust gives us more fine grained control over runtime behavior and memory management. As an added bonus it makes concurrent programming easier and eliminates data races. Its packaging system, Crates.io, is fantastic and includes everything you need out of the box. With the addition of async/await last fall, I thought it was time to start seriously considering it.
Embedded
Thanks to Rust, we can take memory safety for granted, while other benefits of a zero-overhead language with a sophisticated type system help us develop maintainable software. Rust makes our customers happy, as well as our engineers. [...] We think it’s really cool that we can use a modern nice language in the embedded space where usually there’s no alternative to C/C++.
Web Assembly
No unpredictable garbage collection pauses. No JIT compiler performance cliffs. Just low-level control coupled with high-level ergonomics. [...] Small code size means faster page loads. Rust-generated .wasm doesn’t include extra bloat, like a garbage collector. Advanced optimizations and tree shaking remove dead code.
Lucet & Cranelift
Announcing Lucet: Fastly’s native WebAssembly compiler and runtime
Lucet is designed to take WebAssembly beyond the browser, and build a platform for faster, safer execution on Fastly’s edge cloud. [...] Lucet is built on top of the Cranelift code generator. [...] Lucet also happens to be the first project started at Fastly using the Rust programming language, and we’re happy to report that Rust has been a huge success on this project.
Bytecode Alliance
Announcing the Bytecode Alliance: Building a secure by default, composable future for WebAssembly
Today we announce the formation of the Bytecode Alliance, a new industry partnership coming together to forge WebAssembly’s outside-the-browser future by collaborating on implementing standards and proposing new ones. Our founding members are Mozilla, Fastly, Intel, and Red Hat, and we’re looking forward to welcoming many more.
Ruffle
Ruffle – A Flash Player emulator written in Rust
Ruffle runs natively on all modern operating systems as a standalone application, and on all modern browsers through the use of WebAssembly. Leveraging the safety of the modern browser sandbox and the memory safety guarantees of Rust, we can confidently avoid all the security pitfalls that Flash had a reputation for. Ruffle puts Flash back on the web, where it belongs - including iOS and Android!
Web Framework
Yup! Rust has mature and production ready frameworks [...] These provide everything you’d expect from a web framework, from routing and middleware, to templating, and JSON/form handling. There are crates for everything, and more!
Actix
Actix – A powerful, pragmatic, and extremely fast web framework for Rust
Actix is an ecosystem of crates. The base of it is a powerful actor system for Rust on top of which the actix-web system was originally built. [...] If you are already a Rust programmer you will probably find yourself at home quickly, but even if you are coming from another programming language you should find actix-web easy to pick up.
Actix Example
An actix app comes with a URL routing, that lets you match on URLs and invoke individual handlers, and provides a powerful system, that extracts data from the incoming HTTP request and passes it to your view functions.
use actix_web::{get, web, Result}; use serde::Deserialize; #[derive(Deserialize)] struct Info { user_id: u32, friend: String, } /// extract path info using serde #[get("/users/{user_id}/{friend}")] // <- define path parameters async fn index(info: web::Path<Info>) -> Result<String> { Ok(format!( "Welcome {}, user_id {}!", info.friend, info.user_id )) } #[actix_web::main] async fn main() -> std::io::Result<()> { use actix_web::{App, HttpServer}; HttpServer::new(|| App::new().service(index)) .bind("127.0.0.1:8080")? .run() .await }
Actix Benchmark
TechEmpower Web Framework Benchmarks Round 19 (2020-05-28)
Rocket
Rocket – A web framework for Rust
Rocket is a web framework for Rust that makes it simple to write fast, secure web applications without sacrificing flexibility, usability, or type safety.
Gaming
Why Rust is the Future of Game Development
Whether my optimism is naive or not, my gut, and my experience of the Rust community's vibrant enthusiasm, tells me that we are heading towards a tipping point. The threshold beyond which Rust was nowhere and then suddenly it is everywhere. For games, that day might still be far away, but I believe many of the technical hurdles will fall: because smart people enjoy a challenge and because it is there.
John Carmack
Just last night I was thinking about how it was possible that, given the relative trends, Mozilla’s greater legacy might turn out to be Rust, not Firefox.
Rust would be the obvious things, and I don't have any reason to doubt it would be good
Bevy
Bevy - A data-driven game engine built in Rust
A refreshingly simple data-driven game engine built in Rust. Free and Open Source Forever!
Science
Why scientists are turning to Rust
Scientists, too, are turning to Rust. Köster, for instance, used it to create an application, called Varlociraptor, that compares millions of sequence reads against billions of genetic bases to identify genomic variants. “This is huge data,” he says. “So that needs to be as fast as possible.” But that power comes at a cost: the Rust learning curve is steep.
Companies
Mozilla
Mozilla’s first Rust media parser. I’m happy to report that their code will be the first Rust component shipping in Firefox. For the Rust community as well, this is a real achievement: Rust code shipping to hundreds of millions of Firefox users. Our preliminary measurements show the Rust component performing beautifully and delivering identical results to the original C++ component it’s replacing—but now implemented in a memory-safe programming language.
NPM
NPM Adopted Rust to Remove Performance Bottlenecks
In the end, the npm team decided to deploy the Rust version of the authentication service mostly thanks to the strong support they got from the Rust community and to the superiority of Rust package manager Cargo, in comparison with what Go offered at that time. The good news for the npm team is that the Rust service has been running for more than one year in production without a single alert. This is in stark contrast to the usual experience of deploying a Node.js service at npm which includes extensive monitoring to keep errors and resource usage under control.
Figma
We chose Rust for this rewrite because it combines best-in-class speed with low resource usage while still offering the safety of standard server languages. Low resource usage was particularly important to us because some of the performance issues with the old server were caused by the garbage collector.
1Password
1Password – New Filling brain written in Rust
1Password’s filling brain is the technology responsible for autofilling your information. The brain analyzes webpages in the background so it can suggest relevant items to fill in the available fields. In 1Password X 1.17, we’ve completely rewritten the brain in Rust and WebAssembly. Rust gives us a boost in both speed and portability - making it smarter, faster, and more embeddable in all our apps.
Shopify
Shopify – Making Commerce Extensible with WebAssembly
Over the past 15 years, Shopify has built a globally distributed, commerce platform. We have one of the largest Rails monoliths on earth. A core mandate is to keep this codebase simple, only building what most merchants need most of the time. To solve this problem, we have turned to WebAssembly on the server. Our team is building a synchronous customization platform on top of WebAssembly. We are building a developer SDK using AssemblyScript and Lucet that will allow partners to write synchronous plugins to our commerce platform.
OneSignal
Rust enables one to build robust, complex systems quickly and fearlessly thanks to its powerful type system and ownership rules. [...] Given that we now have a production system written in Rust, it's obvious which side of this trade we landed on. Our experience has been positive overall and indeed we have had fantastic results.
Cloudflare
How we made Firewall Rules in Rust
We chose Rust for wirefilter as early in the project we recognised that if we attempted to make implementations of this in Go and Lua, that it would result in inconsistencies that attackers may be able to exploit. [...] With a mixed set of requirements of performance, memory safety, low memory use, and the capability to be part of other products that we’re working on like Spectrum, Rust stood out as the strongest option.
Discord
Why Discord is switching from Go to Rust
Rust is blazingly fast and memory-efficient: with no runtime or garbage collector, it can power performance-critical services, run on embedded devices, and easily integrate with other languages. [...] Rust does not have garbage collection, so we figured it would not have the same latency spikes Go had.
Dropbox
Rewriting the heart of our sync engine
We wrote Nucleus in Rust! Rust has been a force multiplier for our team, and betting on Rust was one of the best decisions we made. More than performance, its ergonomics and focus on correctness has helped us tame sync’s complexity. We can encode complex invariants about our system in the type system and have the compiler check them for us.
Diem (formerly known as Libra) is a permissioned blockchain-based payment system proposed by the American social media company Facebook, Inc. Diem source code is written in Rust and published as open source under the Apache License on GitHub.
Apple
Come help us build cool things in @rustlang at Apple!
The performance and security of the systems we build are critical. We interface directly to low-level Linux kernel interfaces, using asynchronous I/O and threads to distribute workload. Following a very successful first foray into Rust we are migrating an established codebase from C to Rust, and building new functionality primarily in Rust.
Amazon
Why AWS loves Rust, and how we’d like to help
Here at AWS, we love Rust, too, because it helps AWS write highly performant, safe infrastructure-level networking and other systems software. Amazon’s first notable product built with Rust, Firecracker, launched publicly in 2018 and provides the open source virtualization technology that powers AWS Lambda and other serverless offerings. But we also use Rust to deliver services such as Amazon Simple Storage Service (Amazon S3), Amazon Elastic Compute Cloud (Amazon EC2), Amazon CloudFront, Amazon Route 53, and more.
Microsoft
How Microsoft Is Adopting Rust
Microsoft determined that 70% of security patches pushed to computers are to fix memory-related bugs and believes that Rust would’ve been able to catch these bugs during the development phase. As a result, they tasked some engineers to rewrite some components of Windows in Rust to get some developer sentiment.
Chrome
Chrome engineers are experimenting with Rust. For the foreseeable future, C++ is the reigning monarch in our codebase, and any use of Rust will need to fit in with C++ — not the other way around. [...] For now, Chrome investment in Rust will remain a background investigation (mostly directed towards prototyping these tools and techniques). If we become convinced this sort of interoperability is possible, we’ll revisit widespread use of Rust in Chrome, and at that point we plan to work hard to achieve this with robust production-quality solutions.
Linux
Linus Torvalds' Initial Comment On Rust Code Prospects Within The Linux Kernel
Linus commented that he would like it [Rust] to be effectively enabled by default to ensure there is widespread testing and not any isolated usage where developers then may do "crazy" things. He isn't calling for Rust to be a requirement for the kernel but rather if the Rust compiler is detected on the system, Kconfig would enable the Rust support and go ahead in building any hypothetical Rust kernel code in order to see it's properly built at least.
References
Learn Rust contains links to the official documentation
The Book
by Steve Klabnik and Carol Nichols, with contributions from the Rust Community
FREE AVAILABLE ONLINE at doc.rust-lang.org/book
Official Books
-
🦀 Cargo Book - A book on Rust’s package manager and build system
-
🦀 Rustdoc Book - Learn how to make awesome documentation for your crate
-
🦀 Rustc Book - Familiarize yourself with the knobs available in the Rust compiler
-
🦀 Command Line Book - Learn how to build effective command line applications in Rust
-
🦀 Webassembly Book - Use Rust to build browser-native libraries through WebAssembly
-
🦀 Embedded Book - Become proficient with Rust for embedded systems
-
🦀 The Rustonomicon Book - What you need to understand when writing Unsafe Rust
-
🦀 The Reference Book - Not a formal spec, but more detailed than the book
Third Parties Books
Programming Rust
by Jim Blandy, Jason Orendorff | December 2017 | O'Reilly
Programming WebAssembly with Rust
by Kevin Hoffman | March 2019 | PragProg
Rust in Action
by Tim McNamara | (MEAP) Spring 2021 | Manning
Tutorials
-
🏊♀️ Rustlings - Small exercises to get you used to reading and writing idiomatic Rust code
-
🚴♂️ Tour of Rust - A step by step guide through the features of the Rust programming language
-
👨🍳 Rust Cookbook - A collection of good practices to accomplish common programming tasks
-
🏊♀️ Rust by Example - A collection of runnable examples that illustrate various Rust concepts
-
📒 Rust Cheat Sheet - A single-page resource for people who like high information density
-
🕺 Rust for Professionals - A summary for developers that already know another language
-
⏳ A half-hour to learn Rust - Explain what the keywords and symbols in the snippets mean
-
🍼 A Gentle Introduction To Rust - An intro to get enough feeling of Rust to start learning
Community
-
📮 Reddit - A subreddit for all things related to the Rust programming language (120K users)
-
🎮 Discord - Learn how to make awesome documentation for your crate (25K users)
-
🗣 Users forum - A place to communicate about anything related to Rust (16K users)
-
📧 This Week in Rust - Handpicked Rust updates, delivered to your inbox (10K users)
-
🥋 Rust Lang Zulip Chat - The official chat platform for distributed Rust teams (5K users)
-
🇮🇹 Rust Language Milano - Regular meetings on the last wednesday of the month (500 users)
-
💎 codermine.slack.com | #x-rustaceans - For many but not for everyone! (2 users)
Thanks 🙏
Why Rust? by Giuseppe Arici & Gianluca Fioletti