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

ferris

Rust

Hello, [c]rustaceans, I am Ferris the crab, unofficial mascot for Rust.

Rust Mascot

The Rust Buzz

www.rust-lang.org

  • 🥇 By Mozilla For System Programming
  • 🏅 Community Driven and Open Source
  • 🎖 Performance, Reliability, Productivity

Rust Home

The Rust Bits

www.rust-lang.org

  • ⚽️ 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

Rust Logo

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

Mozilla

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

www.rust-lang.org/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.

Governance

Teams

www.rust-lang.org/governance

Teams

The Core Team

www.rust-lang.org/governance/teams/core

Core Team

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.

Science

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!

www.rust-lang.org/tools

Image

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.

Rust Playground

Summary

⚠️ 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

ferris

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)
  • 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
String in memory

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
String in memory

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()
}
String in memory

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
String in memory

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

ferris

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 and match statements

  • A let pulls the variables out into the current scope, whereas match 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 as Option:
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 a where 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 and None


#![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 and Err.


#![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 a Result!

  • 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 identifier PI 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 file mod.rs inside

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 or foo/mod.rs and will insert its contents inside a module named foo 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 crate
    • super the parent module of your current module
    • self 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 or main.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 or Box 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 to false
    • assert_eq!(left, right) and assert_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

ferris

Quick Comparisons

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

Image

Rust 💪 Java

  • No GC Pauses
  • No JVM Overhead
  • Much Lower Memory Use
  • No Null Pointers
  • Zero-Cost Abstractions
  • Pattern Matching
  • Unified Build System

Image

Rust 💪 C/C++

  • No Segfaults
  • No Buffer Overflows
  • No Null Pointers
  • No Data Races
  • Stronger Type System
  • Dependency Management
  • Unified Build System

Image

Rust 💪 Go

  • No GC Pauses
  • Lower Memory Use
  • Nicer Error Handling
  • No Null Pointers
  • Safe Concurrency
  • Stronger Type System
  • Zero-Cost Abstractions

Image

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

ferris

Tools

www.rust-lang.org/tools

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

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

github.com/rust-lang/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

Rustfmt Config

rust-lang.github.io/rustfmt/

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

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

Clippy Config

rust-lang.github.io/clippy/

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

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

https://docs.rs/

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

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

rustdoc output

Libraries

doc.rust-lang.org/std is The Rust Standard Library

Rust tries to avoid stuffing everything into the standard library

The Standard Library

Crates

crates.io is the Rust community’s package registry

crates

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

Awesome Rust

Applications

Rust in production

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.

Rust in production

CLI

CLI in Rust

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.

CLI

mdBook

mdBook documentation system

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.

mdBook

This Source

github.com/codermine/whyrust

This repository contains the source of the Why Rust? presentation.

mdBook

System

System in Rust

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.

System

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.

Servo

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.

Web Framework

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.

InfluxDB

Embedded

Embedded in Rust

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++.

Embedded

Web Assembly

Web­Assembly in Rust

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.

Web Framework

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.

Lucet & Cranelift

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.

Bytecode Alliance

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!

Ruffle

Web Framework

Are we web yet?

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!

Web Framework

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.

Web Framework

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)

Actix Benchmark

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.

Rocket

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.

Gaming

John Carmack

John Carmack on Rust 1

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.

Gaming

John Carmack on Rust 2

Rust would be the obvious things, and I don't have any reason to doubt it would be good

Gaming

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!

Bevy

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.

Science

Companies

Rust in Production

Production

Mozilla

Shipping Rust in Firefox

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.

Firefox

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.

NPM

Figma

Rust in Production at 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.

Figma

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.

1Password

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.

Shopify

OneSignal

Rust at 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.

OneSignal

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.

Cloudflare

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.

Discord

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.

Dropbox

Facebook

The Diem Core Rust codebase

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.

Facebook

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.

Apple

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.

Amazon

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.

Microsoft

Chrome

Rust and C++ interoperability

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.

Chrome

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.

Linux

References

Learn Rust contains links to the official documentation

Tools

The Book

The Rust Programming Language

by Steve Klabnik and Carol Nichols, with contributions from the Rust Community

FREE AVAILABLE ONLINE at doc.rust-lang.org/book

Tools

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 Rust

Programming WebAssembly with Rust

by Kevin Hoffman | March 2019 | PragProg

Programming WebAssembly with Rust

Rust in Action

by Tim McNamara | (MEAP) Spring 2021 | Manning

Rust in Action

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

Image