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:
- Almost 11K crates on Crates.io
- 1,852 contributors on Github.
- Big areas: game dev, operating systems, web development
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
- Crates.io
- Playground
$ 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