1 of 36

Rust

Building blocks for fast code

Aleksey Kladov

JetBrains

aleksey.kladov@gmail.com

github.com/matklad

1

2 of 36

2

3 of 36

There are many interesting programming languages

  • OCaml
  • Elixir
  • Elm
  • Clojure
  • Swift

  • D
  • Nim
  • Ceylon
  • Kotlin
  • Dart

3

But I want to talk about Rust

4 of 36

Small Runtime Large Runtime

C Nim, D Java, Go

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

Assembly C++ Ocaml

4

5 of 36

5

6 of 36

Small Runtime Large Runtime

C Nim, D Java, Go

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

Assembly C++/Rust Ocaml

6

7 of 36

What can you do without GC?

  • Core infrastructure without segfaults
  • Native libraries usable everywhere
  • Embedded devices
  • Rewrite C code incrementally

7

8 of 36

What can you do without GC?

  • Core infrastructure without segfaults
  • Native libraries usable everywhere
  • Embedded devices
  • Rewrite C code incrementally

Not in this talk

8

9 of 36

This talk

Fast code

  • Control over the memory layout
  • Safe parallelism

9

10 of 36

Cache rules

10

11 of 36

ArrayList<Foo>

*

Foo

*

Bar

*

*

Foo

*

Bar

Foo

*

Bar

12 of 36

Ideal ArrayList<Foo>

*

*

*

Foo

Bar

Foo

Bar

Foo

Bar

13 of 36

Modern C++

1 #include <iostream> �2 #include <vector> �3 �4 int main() { �5 std::vector<int> xs = {1, 2, 4, 5};�6 auto& x = xs[0]; �7 xs.push_back(6); �8 std::cout << x << std::endl; �9 }

13

14 of 36

Modern C++

1 #include <iostream> �2 #include <vector> �3 �4 int main() { �5 std::vector<int> xs = {1, 2, 4, 5};�6 auto& x = xs[0]; �7 xs.push_back(6); �8 std::cout << x << std::endl; �9 }

$ clang++ --std=c++14 -Wall -pedantic ./main.cpp

$ ./a.out

0

14

15 of 36

1 fn main() { �2 println!("Hello, World!"); �3 }

15

16 of 36

Rules

  • Ownership:

Every value has a single owner

  • Lifetime:

A reference can not outlive value owner

  • Aliasing:

Shared XOR Mutable

16

17 of 36

Ownership

1 fn main() { �2 let mut xs = vec![1, 2, 3, 4]; // Heap allocated vector �3 xs.push(5); �4 // Drop `xs` here �5 }

17

18 of 36

Ownership

1 struct Foo { �2 ints: Vec<i32> �3 } �4 �5 impl Foo { �6 fn new(xs: Vec<i32>) -> Foo { �7 return Foo { ints: xs }; �8 } �9 }

18

19 of 36

Ownership

1 struct Foo { �2 ints: Vec<i32> �3 } �4 �5 impl Foo { �6 fn new(xs: Vec<i32>) -> Foo { �7 return Foo { ints: xs }; �8 } �9 }

1 fn main() { �2 let mut xs = vec![1, 2, 3, 4]; �3 xs.push(5); �4 let foo = Foo::new(xs); �5 // No `xs` any more! �6 // Drop `foo` here �7 }

19

20 of 36

Ownership

1 struct Foo { �2 ints: Vec<i32> �3 } �4 �5 impl Foo { �6 fn new(xs: Vec<i32>) -> Foo { �7 return Foo { ints: xs }; �8 } �9 }

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

1 fn main() { �2 let mut xs = vec![1, 2, 3, 4]; �3 xs.push(5); �4 let foo = Foo::new(xs); �5 xs.push(92); �6 �7 }

20

21 of 36

References

1 fn sum(xs: &Vec<i32>) -> i32 { �2 xs.iter().sum() �3 } �4 �5 fn main() { �6 let xs = vec![1, 2, 3, 4]; �7 let total = sum(&xs); �8 println!("sum = {}", total); �9 }

21

22 of 36

Shared ^ Mutable

1 fn main() { �2 let mut x: i32 = 92; �3 let r1: &i32 = &x; �4 let r2: &i32 = &x; �5 }

1 fn main() { �2 let mut x: i32 = 92; �3 let r: &mut i32 = &mut x; �4 }

22

23 of 36

Shared ^ Mutable

1 fn main() { �2 let mut x: i32 = 92; �3 let r1: &mut i32 = &mut x; �4 let r2: &i32 = &x; �5 }

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

--> shared_xor_mutable.rs:4:21

|

3 | let r1: &mut i32 = &mut x;

| - mutable borrow occurs here

4 | let r2: &i32 = &x;

| ^ immutable borrow occurs here

5 | }

| - mutable borrow ends here

error: aborting due to previous error

23

24 of 36

Shared ^ Mutable

1 fn main() { �2 let mut xs = vec![1, 2, 3, 4]; �3 let x: &i32 = &xs[0]; �4 xs.push(92); �5 }

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

--> vector.rs:4:5

|

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

| -- immutable borrow occurs here

4 | xs.push(92);

| ^^ mutable borrow occurs here

5 | }

| - immutable borrow ends here

error: aborting due to previous error

24

25 of 36

Shared ^ Mutable

1 fn push_all(xs: &mut Vec<i32>, ys: &Vec<i32>) { �2 for &y in ys { �3 xs.push(y); �4 } �5 } �6 �7 fn main() { �8 let mut xs = vec![1, 2, 3, 4]; �9 let ys = vec![1, 2, 3, 4]; �10 push_all(&mut xs, &ys); �11 }

25

26 of 36

Shared ^ Mutable

1 fn push_all(xs: &mut Vec<i32>, ys: &Vec<i32>) { �2 for &y in ys { �3 xs.push(y); �4 } �5 } �6 �7 fn main() { �8 let mut xs = vec![1, 2, 3, 4]; �9 let ys = vec![1, 2, 3, 4]; �10 push_all(&mut xs, &xs); �11 }

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

26

27 of 36

1 fn main() { �2 std::thread::spawn(|| { �3 println!("Hello, World!"); �4 }); �5 }

27

28 of 36

1 �2 �3 fn main() { �4 let xs = vec![1, 2, 3, 4]; �5 std::thread::spawn(|| { �6 println!("{:?}", xs); �7 }); �8 }

28

29 of 36

1 �2 �3 fn main() { �4 let xs = vec![1, 2, 3, 4]; �5 std::thread::spawn(|| { �6 println!("{:?}", xs); �7 }); �8 }

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

29

30 of 36

1 �2 �3 fn main() { �4 let xs = vec![1, 2, 3, 4]; �5 std::thread::spawn(move || { �6 println!("{:?}", xs); �7 }); �8 // No `xs` here �9 }

30

31 of 36

1 use std::sync::mpsc::channel; �2 �3 fn main() { �4 let (tx, rx) = channel(); �5 �6 std::thread::spawn(move || { �7 let xs = rx.recv().unwrap(); �8 println!("{:?}", xs); �9 }); �10 �11 let xs = vec![1, 2, 3, 4]; �12 tx.send(xs).unwrap(); // No copy here! �13 }

31

32 of 36

1 �2 �3 fn main() { �4 let mut xs = [0, 0, 0, 0]; // Stack allocated array �5 �6 �7 for i in &mut xs { �8 �9 *i += 1; �10 �11 } �12 �13 �14 println!("{:?}", xs); �15 }

32

33 of 36

1 extern crate crossbeam; �2 �3 fn main() { �4 let mut xs = [0, 0, 0, 0]; �5 �6 crossbeam::scope(|scope| { �7 for i in &mut xs { �8 scope.spawn(move || { �9 *i += 1; // Stack of another thread! �10 }); �11 } �12 }); �13 �14 println!("{:?}", xs); �15 }

33

34 of 36

1 extern crate crossbeam; �2 �3 fn main() { �4 let mut xs = [0, 0, 0, 0]; �5 �6 crossbeam::scope(|scope| { �7 for i in &mut xs { �8 scope.spawn(move || *i += 1 ); �9 scope.spawn(move || *i += 1 ); �10 } �11 }); �12 }

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

34

35 of 36

1 fn main() { �2 let xs = std::sync::Mutex::new([0, 0, 0, 0]); // Protect the data �3 crossbeam::scope(|scope| { �4 for _ in 0..10 { �5 scope.spawn(|| { �6 let mut guard = xs.lock().unwrap(); �7 let xs: &mut [i32; 4] = &mut guard; // Can't leak `xs` �8 for i in xs { �9 *i += 1; �10 } �11 }); �12 } �13 }); �14 println!("{:?}", *xs.lock().unwrap()); �15 }

35

36 of 36

Rust is

  • Dense data structures + memory safety
  • Concurrency without data races
  • And a lot more: https://rust-lang.org

36