1
Rust: Reach Further!
Nicholas Matsakis
2
Q. What is Rust?
Q. Why should I care?
3
Q. What is Rust?
Q. Why should I care?
A. High-level code, low-level performance
4
Decisions…
Double free?
Buffer overflow?
Dangling pointers?
Data races?
GC
😱
😱
😱
😱
😀
😀
😀
😱
😎
😎
😎
😎
Control, flexibility 😀 😕 😎
5
Static type system =
Eat your spinach!
Photo credit: Sanjoy Ghosh
https://www.flickr.com/photos/sanjoy/4016632253/
6
Photo credit: Salim Virji
https://www.flickr.com/photos/salim/8594532469/
Static type system = Eat your spinach!
7
The Rust compiler just saved me from a nasty threading bug. I was
working on cage (our open source development tool for Docker apps with
lots of microservices), and I decided to parallelize the routine that
transformed docker-compose.yml files.
8
Performance
class ::String
def blank?
/A[[:space:]]*z/ == self
end
end
Ruby:
964K iter/sec
static VALUE
rb_str_blank_as(VALUE str)
{
rb_encoding *enc;
char *s, *e;
enc = STR_ENC_GET(str);
s = RSTRING_PTR(str);
if (!s || RSTRING_LEN(str) == 0) return Qtrue;
e = RSTRING_END(str);
while (s < e) {
int n;
unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc);
switch (cc) {
case 9:
case 0xa:
case 0xb:
case 0xc:
case 0xd:
case 0x20:
case 0x85:
case 0xa0:
case 0x1680:
case 0x2000:
case 0x2001:
case 0x2002:
case 0x2003:
case 0x2004:
case 0x2005:
case 0x2006:
case 0x2007:
case 0x2008:
case 0x2009:
case 0x200a:
case 0x2028:
case 0x2029:
case 0x202f:
case 0x205f:
case 0x3000:
#if ruby_version_before_2_2()
case 0x180e:
#endif
/* found */
break;
default:
return Qfalse;
}
s += n;
}
return Qtrue;
}
Performance
Ruby:
964K iter/sec
C:
10.5M iter/sec
10x!
https://github.com/SamSaffron/fast_blank
Performance
10
class ::String
def blank?
/A[[:space:]]*z/ == self
end
end
extern “C” fn fast_blank(buf: Buf) -> bool {
buf.as_slice().chars().all(|c| c.is_whitespace())
}
Get Rust
string slice
Get iterator over
each character
Are all characters
whitespace?
Rust:
11M iter/sec
Ruby:
964K iter/sec
C:
10.5M iter/sec
11
High-level, zero-cost abstractions
fn is_whitespace(text: &str) -> bool {
text.chars()
.all(|c| c.is_whitespace())
}
fn load_images(paths: &[PathBuf]) -> Vec<Image> {
paths.par_iter()
.map(|path| Image::load(path))
.collect()
}
12
Q. What is Rust?
Q. Why should I care?
A. You can do more!
A. High-level code, low-level performance
13
Experienced C++ hacker?
Prefer Ruby? JavaScript?
Make and maintain the designs you always
wanted — but could not justify.
Tune up your application and address hot-spots.
Lower memory usage. Add threads without fear.
14
I like Rust because it is boring.
— CJ Silverio, npm CTO
15
Design of Rust
Rust in Production
Rust Community
16
“Must be this
tall to write
multi-threaded
code”
David Baron

Mozilla Distinguished Engineer
Data races
Sharing
Mutation
No ordering
Data race
17
Actor-based languages
(e.g., Erlang, WebWorkers)
Functional languages
(e.g., Haskell)
Sequential programming
Data races
Sharing
Mutation
No ordering
Data race
18
Rust: No sharing and

mutation at the same time.
19
Ownership and BorrowingPhoto Credit: Nathan Kam
https://www.youtube.com/watch?v=Tnssn9KcWLg
Type Ownership Alias? Mutate?
T Owned ✓
~ Ownership and borrowing ~
fn main() {
let apple = Apple::new();
eat(apple);
}
21
fn eat(apple: Apple) {
…
}
Take ownership
of the apple
Give ownership
of the apple.
Owns
Owns
eat(apple);
~~~~~~~~~
Error: `apple` has
been moved.
fn main() {
let apple = Apple::new();
let mut bag = Vec::new();
bag.push(apple);
bag.push(Apple::new());
deliver(bag);
}
22
fn deliver(bag: Vec<Apple>) {
…
}
Take ownership
of the vector
Give ownership.
Give ownership.
Owns
Owns
23
“Manual” memory management in Rust:
Values owned by creator.
Values moved via assignment.
When final owner returns, value is freed.
Feels invisible.
]
~ Ownership and borrowing ~
Type Ownership
T
Alias? Mutate?
Owned ✓
&T Shared reference ✓
fn main() {
let apple = Apple::new();
let mut bag = Vec::new();
bag.push(apple);
bag.push(Apple::new());
let weight = weigh(&bag);
…
}
25
fn weigh(bag: &Vec<Apple>) -> u32 {
…
}
Shared reference
to the vector
Loan out the bag
(Return type)
shared
references
~~~~~~~~~
let mut bag = Vec::new();
bag.push(…);
let r = &bag;
bag.len();
bag.push(…);
r.push(…);
bag.push(…);
reading `bag` ok while shared
cannot mutate while shared
26
Sharing “freezes” data (temporarily)
`bag` mutable here
~~~~~~~~~~~
`bag` borrowed here
after last use of `r`, 

`bag` is mutable again
cannot mutate through shared ref
~ Ownership and borrowing ~
Type Ownership
T
&T
Alias? Mutate?
Owned
Shared reference
✓
✓
&mut T Mutable reference ✓
cannot access `bag` while borrowed
but can mutate through `r`
28
Mutable references: no other access
`bag` mutable here
~~~~~~~~~
`bag` mutably borrowed here
after last use of `r`, `bag` is
accessible again
let mut bag = Vec::new();
bag.push(…);
let r = &mut bag;
bag.len();
r.push(…);
bag.push(…);
29
Parallelism
Photo credit: Dave Gingrich
https://www.flickr.com/photos/ndanger/2744507570/
30
Observation:
Building parallel abstractions is easy.
Misusing those abstractions is also easy.
func foo(…) {
m := make(map[string]string)
m[“Hello”] = “World”
channel <- m
m[“Hello”] = “Data Race”
}
send data over channel
but how to stop sender from

using it afterwards?
GoCode
31
fn foo(…) {
let m = HashMap::new();
m.insert(“Hello”, “World”);
channel.send(m);
m.insert(“Hello”, “Data Race”);
}
impl<T> Channel<T> {
fn send(&mut self, data: T) {
…
}
}
Take ownership
of the data
Error: use of moved
value: `book`
~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Concurrency paradigms ~
Paradigm
Message passing
Ownership? Borrowing?
✓
Fork join ✓
33
fn load_images(paths: &[PathBuf]) -> Vec<Image> {
paths.iter()
.map(|path| {
Image::load(path)
})
.collect()
}
For each path…
…load an image…
…create and return
a vector.
paths = [ “a.jpg”, “b.png”, …, “c.jpg” ]
borrowed from caller
34
fn load_images(paths: &[PathBuf]) -> Vec<Image> {
paths.par_iter()
.map(|path| {
Image::load(path)
})
.collect()
}
Make it parallel
paths = [ “a.jpg”, “b.png”, …, “c.jpg” ]
extern crate rayon; Third-party library
35
Observation:
Building parallel abstractions is easy.
Misusing those abstractions is also easy.
36
fn load_images(paths: &[PathBuf]) -> Vec<Image> {
let mut jpgs = 0;
paths.par_iter()
.map(|path| {
if path.ends_with(“.jpg”) { jpgs += 1; }
Image::load(path)
})
.collect()
}
How many jpgs seen so far?
…add 1 to the counter.
If current file name ends in “jpg”…
0
0
+ 1
0
+ 1
1 1
1
fn load_images(paths: &[PathBuf]) -> Vec<Image> {
let mut jpgs = 0;
paths.par_iter()
.map( )
.collect()
}
37
borrows
mutably
borrows
mutably
|path| {
if path.ends_with(“.jpg”) {
jpgs += 1;
}
Image::load(path)
}
|path| {
if path.ends_with(“.jpg”) {
jpgs += 1;
}
Image::load(path)
}
~~~~~~~~~❌ ~~~~~~~~~❌
~ Concurrency paradigms ~
Paradigm
Message passing
Ownership? Borrowing?
✓
Fork join ✓
Lock-free
Futures
…
✓ ✓
✓ ✓
Locking ✓ ✓
39
Unsafe
40
Ownership and Borrowing
Parallelism
File
System
Referencecount
Rust: An Extensible Language
Core Language
Libraries
Safe abstractions
unsafe {
…
}
Ownership/borrowing/traits give tools to
enforce safe abstraction boundaries.
Trust me.
fn split_at_mut(…) {
}
Validates input, etc.
41
42
Stylo (Parallel CSS Rendering — coming in FF57)
Total KLOC Unsafe KLOC Unsafe %
Total 146.2 51.7 35%
Interesting stuff 71.6 1.4 1.9%
FFI Bindings 74.5 50.3 67.4%
43
Photo credit: Don Urban
https://www.flickr.com/photos/donpezzano/3044965125/
Integration
44
usehelix.com
Rubynode.js
neon-bindings/neon
PyO3/pyo3
dgrunwald/rust-cpython
CPython
getsentry/milksnake
45
ArrayBuffer
Worker
46
ArrayBuffer fn callback(mut vm: CallContext) {
let buffer = vm.arguments(0);
let guard: VmGuard = vm.lock();
let data = buffer.borrow_mut(&guard);
data
.par_iter_mut()
.for_each(|i| *i += 1);
}
vm
guard
borrows
data
buffer
data
47
ArrayBuffer
buffer
data
What could go wrong?
What if we invoked JS
callback while using `data`?
▶ Can’t: Invoking JS
callbacks requires mutable
borrow of `vm`!
vm
guard
borrow
data
48https://medium.com/@wireapp/3ff37fc98c3f
49
ruby! {
class Console {
def log(string: &str) {
println!("LOG: {:?}", string);
}
}
}
50
Rust in Production
51
Will discuss today!
Whitepapers
available online.
rust-lang.org/en-US/whitepapers.html
52
Category:
Experienced system devs
53
54
55
Photo credit: Salim Virji
https://www.flickr.com/photos/salim/8594532469/
Safety = Eat your spinach!
56
Gradual adoption
works.
57
Category:
Full-stack developers
58
"Because our product helps people identify why their
apps are slow, it is very important that we
ourselves do not make their app slow," says
Yehuda Katz, CTO of Tilde.
Performance monitoring software for RoR.
Written (initially) in Ruby, but hit performance limitations:
59
Why Rust?
Next, they prototyped the agent in C++. Although the
C++ implementation used fewer resources, it also
introduced an unacceptable risk of crashes.
Katz and his team spent time squeezing every drop
of performance out of Ruby including removing
uses of higher-level features of the language that
required more resources.
A. C++ without crashes means the whole
team can write performance critical code.
60
61
Community
Photo credit: David McSpadden
https://www.flickr.com/photos/familyclan/15535822737/
62
Focus: Productivity and Ergonomics
Culmination of 2017 roadmap efforts across the board:
documentation, language, tooling, libraries, etc.
63
RFC Process
64
Rust Teams
Core Team
Language Team
Library Team
Compiler Team
Dev Tools Team
Cargo Team
IDEs and Editor Team
Infrastructure Team
Release Team
Community Team
Documentation Team
Rustdoc Team
Moderation Team
Mostly open-source volunteers.
Mozilla employees are a small fraction.
65
Rust Domain Working Groups
Web Services
WebAssembly
CLI Apps
Embedded Devices
66
From http://jvns.ca/blog/2016/09/11/rustconf-keynote/
Open and welcoming
67
Want to learn more?
intorust.com
rust-lang.org
O’Reilly
(Screencasts)
Manning
No Starch
Armstrong
github.com/ctjhoa/rust-learning
Tons more at:

Rust: Reach Further (from QCon Sao Paolo 2018)

  • 1.
  • 2.
    2 Q. What isRust? Q. Why should I care?
  • 3.
    3 Q. What isRust? Q. Why should I care? A. High-level code, low-level performance
  • 4.
    4 Decisions… Double free? Buffer overflow? Danglingpointers? Data races? GC 😱 😱 😱 😱 😀 😀 😀 😱 😎 😎 😎 😎 Control, flexibility 😀 😕 😎
  • 5.
    5 Static type system= Eat your spinach! Photo credit: Sanjoy Ghosh https://www.flickr.com/photos/sanjoy/4016632253/
  • 6.
    6 Photo credit: SalimVirji https://www.flickr.com/photos/salim/8594532469/ Static type system = Eat your spinach!
  • 7.
    7 The Rust compilerjust saved me from a nasty threading bug. I was working on cage (our open source development tool for Docker apps with lots of microservices), and I decided to parallelize the routine that transformed docker-compose.yml files.
  • 8.
  • 9.
    static VALUE rb_str_blank_as(VALUE str) { rb_encoding*enc; char *s, *e; enc = STR_ENC_GET(str); s = RSTRING_PTR(str); if (!s || RSTRING_LEN(str) == 0) return Qtrue; e = RSTRING_END(str); while (s < e) { int n; unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); switch (cc) { case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0x20: case 0x85: case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: case 0x2028: case 0x2029: case 0x202f: case 0x205f: case 0x3000: #if ruby_version_before_2_2() case 0x180e: #endif /* found */ break; default: return Qfalse; } s += n; } return Qtrue; } Performance Ruby: 964K iter/sec C: 10.5M iter/sec 10x! https://github.com/SamSaffron/fast_blank
  • 10.
    Performance 10 class ::String def blank? /A[[:space:]]*z/== self end end extern “C” fn fast_blank(buf: Buf) -> bool { buf.as_slice().chars().all(|c| c.is_whitespace()) } Get Rust string slice Get iterator over each character Are all characters whitespace? Rust: 11M iter/sec Ruby: 964K iter/sec C: 10.5M iter/sec
  • 11.
    11 High-level, zero-cost abstractions fnis_whitespace(text: &str) -> bool { text.chars() .all(|c| c.is_whitespace()) } fn load_images(paths: &[PathBuf]) -> Vec<Image> { paths.par_iter() .map(|path| Image::load(path)) .collect() }
  • 12.
    12 Q. What isRust? Q. Why should I care? A. You can do more! A. High-level code, low-level performance
  • 13.
    13 Experienced C++ hacker? PreferRuby? JavaScript? Make and maintain the designs you always wanted — but could not justify. Tune up your application and address hot-spots. Lower memory usage. Add threads without fear.
  • 14.
    14 I like Rustbecause it is boring. — CJ Silverio, npm CTO
  • 15.
    15 Design of Rust Rustin Production Rust Community
  • 16.
    16 “Must be this tallto write multi-threaded code” David Baron
 Mozilla Distinguished Engineer
  • 17.
    Data races Sharing Mutation No ordering Datarace 17 Actor-based languages (e.g., Erlang, WebWorkers) Functional languages (e.g., Haskell) Sequential programming
  • 18.
    Data races Sharing Mutation No ordering Datarace 18 Rust: No sharing and
 mutation at the same time.
  • 19.
    19 Ownership and BorrowingPhotoCredit: Nathan Kam https://www.youtube.com/watch?v=Tnssn9KcWLg
  • 20.
    Type Ownership Alias?Mutate? T Owned ✓ ~ Ownership and borrowing ~
  • 21.
    fn main() { letapple = Apple::new(); eat(apple); } 21 fn eat(apple: Apple) { … } Take ownership of the apple Give ownership of the apple. Owns Owns eat(apple); ~~~~~~~~~ Error: `apple` has been moved.
  • 22.
    fn main() { letapple = Apple::new(); let mut bag = Vec::new(); bag.push(apple); bag.push(Apple::new()); deliver(bag); } 22 fn deliver(bag: Vec<Apple>) { … } Take ownership of the vector Give ownership. Give ownership. Owns Owns
  • 23.
    23 “Manual” memory managementin Rust: Values owned by creator. Values moved via assignment. When final owner returns, value is freed. Feels invisible. ]
  • 24.
    ~ Ownership andborrowing ~ Type Ownership T Alias? Mutate? Owned ✓ &T Shared reference ✓
  • 25.
    fn main() { letapple = Apple::new(); let mut bag = Vec::new(); bag.push(apple); bag.push(Apple::new()); let weight = weigh(&bag); … } 25 fn weigh(bag: &Vec<Apple>) -> u32 { … } Shared reference to the vector Loan out the bag (Return type) shared references
  • 26.
    ~~~~~~~~~ let mut bag= Vec::new(); bag.push(…); let r = &bag; bag.len(); bag.push(…); r.push(…); bag.push(…); reading `bag` ok while shared cannot mutate while shared 26 Sharing “freezes” data (temporarily) `bag` mutable here ~~~~~~~~~~~ `bag` borrowed here after last use of `r`, 
 `bag` is mutable again cannot mutate through shared ref
  • 27.
    ~ Ownership andborrowing ~ Type Ownership T &T Alias? Mutate? Owned Shared reference ✓ ✓ &mut T Mutable reference ✓
  • 28.
    cannot access `bag`while borrowed but can mutate through `r` 28 Mutable references: no other access `bag` mutable here ~~~~~~~~~ `bag` mutably borrowed here after last use of `r`, `bag` is accessible again let mut bag = Vec::new(); bag.push(…); let r = &mut bag; bag.len(); r.push(…); bag.push(…);
  • 29.
    29 Parallelism Photo credit: DaveGingrich https://www.flickr.com/photos/ndanger/2744507570/
  • 30.
    30 Observation: Building parallel abstractionsis easy. Misusing those abstractions is also easy. func foo(…) { m := make(map[string]string) m[“Hello”] = “World” channel <- m m[“Hello”] = “Data Race” } send data over channel but how to stop sender from
 using it afterwards? GoCode
  • 31.
    31 fn foo(…) { letm = HashMap::new(); m.insert(“Hello”, “World”); channel.send(m); m.insert(“Hello”, “Data Race”); } impl<T> Channel<T> { fn send(&mut self, data: T) { … } } Take ownership of the data Error: use of moved value: `book` ~~~~~~~~~~~~~~~~~~~~~~~~~~
  • 32.
    ~ Concurrency paradigms~ Paradigm Message passing Ownership? Borrowing? ✓ Fork join ✓
  • 33.
    33 fn load_images(paths: &[PathBuf])-> Vec<Image> { paths.iter() .map(|path| { Image::load(path) }) .collect() } For each path… …load an image… …create and return a vector. paths = [ “a.jpg”, “b.png”, …, “c.jpg” ] borrowed from caller
  • 34.
    34 fn load_images(paths: &[PathBuf])-> Vec<Image> { paths.par_iter() .map(|path| { Image::load(path) }) .collect() } Make it parallel paths = [ “a.jpg”, “b.png”, …, “c.jpg” ] extern crate rayon; Third-party library
  • 35.
    35 Observation: Building parallel abstractionsis easy. Misusing those abstractions is also easy.
  • 36.
    36 fn load_images(paths: &[PathBuf])-> Vec<Image> { let mut jpgs = 0; paths.par_iter() .map(|path| { if path.ends_with(“.jpg”) { jpgs += 1; } Image::load(path) }) .collect() } How many jpgs seen so far? …add 1 to the counter. If current file name ends in “jpg”… 0 0 + 1 0 + 1 1 1 1
  • 37.
    fn load_images(paths: &[PathBuf])-> Vec<Image> { let mut jpgs = 0; paths.par_iter() .map( ) .collect() } 37 borrows mutably borrows mutably |path| { if path.ends_with(“.jpg”) { jpgs += 1; } Image::load(path) } |path| { if path.ends_with(“.jpg”) { jpgs += 1; } Image::load(path) } ~~~~~~~~~❌ ~~~~~~~~~❌
  • 38.
    ~ Concurrency paradigms~ Paradigm Message passing Ownership? Borrowing? ✓ Fork join ✓ Lock-free Futures … ✓ ✓ ✓ ✓ Locking ✓ ✓
  • 39.
  • 40.
  • 41.
    Safe abstractions unsafe { … } Ownership/borrowing/traitsgive tools to enforce safe abstraction boundaries. Trust me. fn split_at_mut(…) { } Validates input, etc. 41
  • 42.
    42 Stylo (Parallel CSSRendering — coming in FF57) Total KLOC Unsafe KLOC Unsafe % Total 146.2 51.7 35% Interesting stuff 71.6 1.4 1.9% FFI Bindings 74.5 50.3 67.4%
  • 43.
    43 Photo credit: DonUrban https://www.flickr.com/photos/donpezzano/3044965125/ Integration
  • 44.
  • 45.
  • 46.
    46 ArrayBuffer fn callback(mutvm: CallContext) { let buffer = vm.arguments(0); let guard: VmGuard = vm.lock(); let data = buffer.borrow_mut(&guard); data .par_iter_mut() .for_each(|i| *i += 1); } vm guard borrows data buffer data
  • 47.
    47 ArrayBuffer buffer data What could gowrong? What if we invoked JS callback while using `data`? ▶ Can’t: Invoking JS callbacks requires mutable borrow of `vm`! vm guard borrow data
  • 48.
  • 49.
    49 ruby! { class Console{ def log(string: &str) { println!("LOG: {:?}", string); } } }
  • 50.
  • 51.
    51 Will discuss today! Whitepapers availableonline. rust-lang.org/en-US/whitepapers.html
  • 52.
  • 53.
  • 54.
  • 55.
    55 Photo credit: SalimVirji https://www.flickr.com/photos/salim/8594532469/ Safety = Eat your spinach!
  • 56.
  • 57.
  • 58.
    58 "Because our producthelps people identify why their apps are slow, it is very important that we ourselves do not make their app slow," says Yehuda Katz, CTO of Tilde. Performance monitoring software for RoR. Written (initially) in Ruby, but hit performance limitations:
  • 59.
    59 Why Rust? Next, theyprototyped the agent in C++. Although the C++ implementation used fewer resources, it also introduced an unacceptable risk of crashes. Katz and his team spent time squeezing every drop of performance out of Ruby including removing uses of higher-level features of the language that required more resources. A. C++ without crashes means the whole team can write performance critical code.
  • 60.
  • 61.
    61 Community Photo credit: DavidMcSpadden https://www.flickr.com/photos/familyclan/15535822737/
  • 62.
    62 Focus: Productivity andErgonomics Culmination of 2017 roadmap efforts across the board: documentation, language, tooling, libraries, etc.
  • 63.
  • 64.
    64 Rust Teams Core Team LanguageTeam Library Team Compiler Team Dev Tools Team Cargo Team IDEs and Editor Team Infrastructure Team Release Team Community Team Documentation Team Rustdoc Team Moderation Team Mostly open-source volunteers. Mozilla employees are a small fraction.
  • 65.
    65 Rust Domain WorkingGroups Web Services WebAssembly CLI Apps Embedded Devices
  • 66.
  • 67.
    67 Want to learnmore? intorust.com rust-lang.org O’Reilly (Screencasts) Manning No Starch Armstrong github.com/ctjhoa/rust-learning Tons more at: