1 of 38

Rust

Alexey Kladov

2 of 38

There are

  • Clojure
  • Elixir
  • Elm
  • Erlang
  • Swift

  • D
  • Nim
  • Ceylon
  • Kotlin
  • Dart

But I want to talk about Rust

3 of 38

Small Runtime Large Runtime

C Nim, D Java, Go

|-----|---|-----<--- GC Gap --->-----|----|--------|--------->

Assembly C++ Ocaml

4 of 38

Small Runtime Large Runtime

C Nim, D Java, Go

|-----|---|-----<--- GC Gap --->-----|----|--------|--------->

Assembly C++/Rust Ocaml

5 of 38

Resource management

  • C — goto, discipline
  • C++ — RAII, discipline
  • C++11 — RAII, move semantic, core guidelines, discipline
  • Rust — RAII + move + affine types + region analysis + strict aliasing rules

No GC, no undefined behaviour, no iterator invalidation, no data races.

Zero overhead in most cases.

6 of 38

Safety

7 of 38

Concurrency

No data races

Mutable state

Not a joke

8 of 38

* Rust uses LLVM, C/C++ uses gcc

9 of 38

Language does not matter (R.I.P. Dylan)

  • Community
  • Solid tools
  • Serious backwards compatibility
  • C binary compatibility
  • Open development process
  • Mozilla

10 of 38

Rust sucks

  • No real incremental compilation
  • No stable C++ ABI (C++ sucks; ⅔ of Servo's binary is C++)
  • No killer app like Docker/Rails (Servo?)
  • Learning curve (not easy at all. Still simpler than C++)

11 of 38

fn main() {

println!("Hello, World!");

}

12 of 38

The mix

C++

  • RAII
  • Static dispatch
  • Monomorphisation/templates
  • Syntax
  • OO/GP feeling

ML

  • Expressions/Statements
  • Immutable by default / first-class mutation
  • Sum types
  • Confidence?
  • Weird semicolon
  • Functional feeling

Cyclone

  • Region analysis

13 of 38

Rules

  1. Ownership:

A value has exactly one owner (at any given time-point)

  • Liveness:

A reference does not outlive the owner

  • Aliasing:

Shared xor mutable

14 of 38

Ownership

fn main() {

let mut xs = vec![1, 2, 3]; // Heap allocated vector

xs.push(4);

// Drop `xs` here

}

15 of 38

Ownership

struct Foo {

ints: Vec<i32>

}

impl Foo {

fn new(xs: Vec<i32>) -> Foo {

return Foo { ints: xs };

}

}

16 of 38

Ownership

struct Foo {

ints: Vec<i32>

}

impl Foo {

fn new(xs: Vec<i32>) -> Foo {

return Foo { ints: xs };

}

}

fn main() {

let mut xs = vec![1, 2, 3];

xs.push(4);

let foo = Foo::new(xs);

// Drop `foo` here

}

17 of 38

Ownership

struct Foo {

ints: Vec<i32>

}

impl Foo {

fn new(xs: Vec<i32>) -> Foo {

return Foo { ints: xs };

}

}

error: use of moved value: `xs` [E0382]

xs.push(92)

^~

help: run `rustc --explain E0382` to see a detailed explanation

fn main() {

let mut xs = vec![1, 2, 3];

xs.push(4);

let foo = Foo::new(xs);

xs.push(92);

}

18 of 38

Borrowing

fn sum(xs: &Vec<i32>) -> i32 {

xs.iter().sum()

}

fn main() {

let xs = vec![1, 2, 3];

let total = sum(&xs);

println!("sum = {}", total);

}

19 of 38

Shared ^ Mutable

fn main() {

let mut x: i32 = 92;

let r1: &i32 = &x;

let r2: &i32 = &x;

}

fn main() {

let mut x: i32 = 92;

let r: &mut i32 = &mut x;

}

20 of 38

Shared ^ Mutable

fn main() {

let mut x: i32 = 92;

let r1: &mut i32 = &mut x;

let r2: &i32 = &x;

}

error: cannot borrow `x` as immutable because it is also borrowed as mutable

let r2: &i32 = &x;

^

note: previous borrow of `x` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `x` until the borrow ends

let r1: &mut i32 = &mut x;

^

21 of 38

Shared ^ Mutable

fn main() {

let mut xs = vec![1, 2, 3];

let x: &i32 = &xs[0];

xs.push(92);

}

error: cannot borrow `xs` as mutable because it is also borrowed as immutable

xs.push(92);

^~

note: previous borrow of `xs` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `xs` until the borrow ends

let x: &i32 = &xs[0];

22 of 38

Shared ^ Mutable

fn push_all(xs: &mut Vec<i32>, ys: &Vec<i32>) {

for &y in ys {

xs.push(y);

}

}

fn main() {

let mut xs = vec![1, 2, 3];

let ys = vec![1, 2, 3];

push_all(&mut xs, &ys);

}

23 of 38

Shared ^ Mutable

fn push_all(xs: &mut Vec<i32>, ys: &Vec<i32>) {

for &y in ys {

xs.push(y);

}

}

fn main() {

let mut xs = vec![1, 2, 3];

let ys = vec![1, 2, 3];

push_all(&mut xs, &xs);

}

error: cannot borrow `xs` as immutable because it is also borrowed as mutable

24 of 38

fn main() {

std::thread::spawn(|| {

println!("Hello, World!");

});

}

25 of 38

fn main() {

let xs = vec![1, 2, 3];

std::thread::spawn(|| {

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

});

}

26 of 38

fn main() {

let xs = vec![1, 2, 3];

std::thread::spawn(|| {

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

});

}

error: closure may outlive the current function, but it borrows `xs`, which is owned by the current function

27 of 38

fn main() {

let xs = vec![1, 2, 3];

std::thread::spawn(move || {

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

});

// No `xs` here

}

28 of 38

use std::sync::mpsc::channel;

fn main() {

let (tx, rx) = channel();

std::thread::spawn(move || {

let xs = rx.recv().unwrap();

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

});

let xs = vec![1, 2, 3];

tx.send(xs).unwrap(); // No copy here!

}

29 of 38

fn main() {

let mut xs = [0, 0, 0, 0]; // Stack allocated array

for i in &mut xs {

*i += 1;

}

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

}

30 of 38

extern crate crossbeam;

fn main() {

let mut xs = [0, 0, 0, 0];

crossbeam::scope(|scope| {

for i in &mut xs {

scope.spawn(move || {

*i += 1; // Stack of another thread!

});

}

});

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

}

31 of 38

extern crate crossbeam;

fn main() {

let mut xs = [0, 0, 0, 0];

crossbeam::scope(|scope| {

for i in &mut xs {

scope.spawn(move || {

*i += 1; // Stack of another thread!

});

}

});

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

}

32 of 38

extern crate crossbeam;

fn main() {

let mut xs = [0, 0, 0, 0];

crossbeam::scope(|scope| {

for i in &mut xs {

scope.spawn(move || *i += 1 );

scope.spawn(move || *i += 1 );

}

});

}

error: capture of moved value: `i` [E0382]

33 of 38

extern crate crossbeam;

fn main() {

let mut xs = [0, 0, 0, 0];

crossbeam::scope(|scope| {

for i in &mut xs {

scope.spawn(move || *i += 1 );

scope.spawn(move || *i += 1 );

}

});

}

We’re borrowing xs here

We’re borrowing i (which is an iterator for xs) here

And here...

34 of 38

35 of 38

extern crate crossbeam;

fn main() {

let mut xs = [0, 0, 0, 0];

crossbeam::scope(|scope| {

for i in &mut xs {

scope.spawn(move || *i += 1 );

scope.spawn(move || *i += 1 );

}

});

}

error: capture of moved value: `i` [E0382]

We’re borrowing xs here

We’re borrowing i (which is an iterator for xs) here

And here...

36 of 38

fn main() {

let xs = std::sync::Mutex::new([0, 0, 0, 0]); // Protect the data

crossbeam::scope(|scope| {

for _ in 0..10 {

scope.spawn(|| {

let mut guard = xs.lock().unwrap();

let xs: &mut [i32; 4] = &mut guard; // Can't leak `xs`

for i in xs {

*i += 1;

}

});

}

});

println!("{:?}", *xs.lock().unwrap());

}

37 of 38

extern crate rayon;

fn quick_sort(xs: &mut[i32]) {

if xs.len() <= 1 { return }

let mid = partition(xs);

let (lo, hi) = xs.split_at_mut(mid);

rayon::join(|| quick_sort(lo), || quick_sort(hi));

}

fn partition(xs: &mut[i32]) -> usize { /* … */ }

fn main() {

let mut xs = [1, 3, 0, 6, 2, 4, 92];

quick_sort(&mut xs);

}

38 of 38

Links