Let's get Rusty

Matt Gathu

https://slides.com/mattgathu/rusty

Rust is a systems  language that pursues the trifecta of:

  • speed

  • safety

  • concurrency

A Brief History

The Graydon Years (2006-2010)

Graydon Hoare's side project.

 

I have been writing a compiled, concurrent, safe, systems programming language for the past four and a half years.

~ July 2010

  • Memory Safety
  • Typestate System
  • Mutability Control
  • Side-effect Control
  • Ocaml compiler

The Mozilla Years (2009-2015)

Mozilla starts sponsoring Rust in 2009.

 

  • Typesystem
  • Ownership
  • Concurrency
  • Cargo & Crates.io
  • RFC process
  • Rust compiler
  • 1.0.0 Release (2015)

Post 1.0.0 (2015-)

Mozilla still the main sponsor.

 

Community contribution:

Speed

  • No Garbage Collection
  • LLVM
  • Zero cost abstractions
  • Minimal Runtime
  • Compile time generics monomorphization

No GC

Rust knows when the variable gets out of scope or its lifetime ends at compile time. (RAII)

 

Rust also allows some kind of garbage collection e.g. Reference Counting

LLVM Integration

Zero Cost Abstractions

C++ implementations obey the zero-overhead principle: What you don’t use, you don’t pay for [Stroustrup, 1994]. And further: What you do use, you couldn’t hand code any better.

– Stroustrup

 

None of Rust’s abstractions impose a global performance penalty

let a = [1, 2, 3];

let sum = a.iter().fold(0, |acc, &x| acc + x);
array = [1, 2, 3]

sum = array.inject(0){|sum,x| sum + x }

RUST

RUBY

Minimal Runtime

  • Again, no garbage collector.

  • Can compile without stdlib

Generics monomorphization

Monomorphisation specializes each use of a generic function with specific instance, based on the parameter types of calls to that function. (similar to C++)

 

Results in fast code that is specialized for every call-site and statically dispatched.

Safety

  • Memory safety
    • You never explicitly free memory.
  • Automatic Resource Management
    • You never explicitly release resources.

 

Rust means never having to close a socket
~ Yehuda Katz

The Ownership System

 

Memory is managed through a set of rules that the compiler checks at compile time. No run-time costs are incurred for any of the ownership features.

 

Rules of Ownership

 
  • Each value has its owner.
  • There can only be one owner at a time.
  • When the owner goes out of scope, the value will be dropped.

 

fn as_str(data: &u32) -> &str {
    // compute the string
    let s = format!("{}", data);

    // (this does not compile in Rust)
    &s
}
// s goes out of scope and is freed here

A reference cannot outlive its referent.

let mut data = vec![1, 2, 3];
// get an internal reference
let x = &data[0];

// (this does not compile in Rust)
data.push(4);

println!("{}", x);

A mutable reference cannot be aliased.

 

A mutable reference is aliased if there exists another live reference to one of its ancestors or descendants.

Concurrency

  • Fearless Concurrency

    • Threads.
    • Message Passing.
    • Shared State.
    • Extensible Concurrency

Threads

Rust supports 1:1 threading model (OS threads).

 

It has NO green threading model.

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
    }

    let _ = handle.join();
}

Message Passing

Threads or actors communicate by sending each other messages containing data.

 

Do not communicate by sharing memory; instead, share memory by communicating.

 

use std::thread;
use std::sync::mpsc;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

Shared State

Mutexes

use std::sync::{Mutex, Arc};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = counter.clone();
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();

            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());
}

Extensible Concurrency

Much of Rust's concurrency is implemented in the standard library, not the language itself.

 

This allows for extensibility.

Send & Sync

The Send and Sync traits are used for concurrency.

 

The Send marker trait indicates that ownership of that type may be transferred between threads. Almost every Rust type is Send.

 

The Sync marker trait indicates that a type is safe to have references to a value from multiple threads. (thread safe)

Convenience

  • high level abstractions
  • type inference
  • functional style support
  • rust once, run everywhere
  • rustfmt
void RemoveSpaces(char* source)
{
  char* i = source;
  char* j = source;
  while(*j != 0)
  {
    *i = *j++;
    if(*i != ' ')
      i++;
  }
  *i = 0;
}
fn remove_spaces(s: &str) -> String {
    s.replace(" ", "")
}

HIGH LEVEL ABSTRACTION

fn main() {
    
    let elem = 5u8;

    
    let mut vec = Vec::new();

    vec.push(elem);

    println!("{:?}", vec);
}

TYPE INFERENCE

Functional Support

  • Closures
  • Iterators
  • Traits
fn main() {
let mut v = [5, 4, 1, 3, 2];

v.sort_by(|a, b| a.cmp(b));

assert!(v == [1, 2, 3, 4, 5]);

// reverse sorting
v.sort_by(|a, b| b.cmp(a));

assert!(v == [5, 4, 3, 2, 1]);

}

CLOSURES

#![feature(inclusive_range_syntax)]

for i in (0...10).rev().filter(|x| (x % 2 == 0)) {
  print!("{} ", i);
}
// output: 10 8 6 4 2 0
let cities = ["Nairobi", "Mombasa", "Kisumu"];
let populations = [2_750_547, 799_668, ‎216_479];

let matrix = cities.iter().zip(populations.iter());

for (c, p) in matrix {
  println!("{:10}: population = {}", c, p);
}
// output:
// Nairobi   : population = 2750547
// Mombasa   : population = 799668
// Kisumu    : population = 216479

ITERATORS

pub trait Summarizable {
    fn summary(&self) -> String;
}
pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summarizable for Tweet {
    fn summary(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

TRAITS

Rust Once, Run Everywhere

Interoperability with other languages using a Foreign Function Interface (FFI)

from ctypes import cdll

lib = cdll.LoadLibrary('target/debug/libdouble_input.dylib')
double_input = lib.double_input

input = 4
output = double_input(input)
print('{} * 2 = {}'.format(input, output))
#[no_mangle]
pub extern fn double_input(input: i32) -> i32 {
    input * 2
}

RUST

PYTHON

Ecosystem

$ cargo new hello_world --bin

cargo new

$ cd hello_world
$ tree .
.
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files

Cargo.toml

[package]
name = "hello_world"
version = "0.1.0"
authors = ["Matt Gathu <mattgathu@gmail.com>"]

src/main.rs

fn main() {
    println!("Hello, world!");
}

cargo build

$ cargo build
   Compiling hello_world v0.1.0 (file:../hello_world)
$ cargo run
     Fresh hello_world v0.1.0 (file:../hello_world)
   Running `target/hello_world`
Hello, world!

Getting Started

curl https://sh.rustup.rs -sSf | sh

https://doc.rust-lang.org/book/

Stay Oxidized!

Let's get Rusty

By Matt Gathu

Let's get Rusty

An overview of the Rust programming language

  • 1,194