Rust: Reach F us th es ! Nich om as Matsakis 1 Q. What is Rust? Q. - - PowerPoint PPT Presentation

rust reach f us th es
SMART_READER_LITE
LIVE PREVIEW

Rust: Reach F us th es ! Nich om as Matsakis 1 Q. What is Rust? Q. - - PowerPoint PPT Presentation

Rust: Reach F us th es ! Nich om as Matsakis 1 Q. What is Rust? Q. Why should I care? 2 Q. What is Rust? A. High-level code, low-level performance Q. Why should I care? 3 Decisions GC Control, flexibility


slide-1
SLIDE 1

1

Rust: Reach Fusthes!

Nichomas Matsakis

slide-2
SLIDE 2

2

  • Q. What is Rust?
  • Q. Why should I care?
slide-3
SLIDE 3

3

  • Q. What is Rust?
  • Q. Why should I care?
  • A. High-level code, low-level performance
slide-4
SLIDE 4

4

Decisions…

Double free? Buffer overflow? Dangling pointers? Data races? GC

😲 😲 😲 😲 😁 😁 😁 😲 😏 😏 😏 😏

Control, flexibility

😁 😖 😏

slide-5
SLIDE 5

5

Static type system = Eat your spinach!

Photo credit: Sanjoy Ghosh https://www.flickr.com/photos/sanjoy/4016632253/
slide-6
SLIDE 6

6

Photo credit: Salim Virji https://www.flickr.com/photos/salim/8594532469/

Static type system = Eat your spinach!

slide-7
SLIDE 7

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.

slide-8
SLIDE 8

8

Performance

class ::String def blank? /\A[[:space:]]*\z/ == self end end

Ruby: 964K iter/sec

slide-9
SLIDE 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

slide-10
SLIDE 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

slide-11
SLIDE 11

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() }

slide-12
SLIDE 12

12

  • Q. What is Rust?
  • Q. Why should I care?
  • A. You can do more!
  • A. High-level code, low-level performance
slide-13
SLIDE 13

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.

slide-14
SLIDE 14

14

I like Rust because it is boring. — CJ Silverio, npm CTO

slide-15
SLIDE 15

15

Design of Rust Rust in Production Rust Community

slide-16
SLIDE 16

16

“Must be this tall to write multi-threaded code”

David Baron
 Mozilla Distinguished Engineer

slide-17
SLIDE 17

Data races

Sharing Mutation No ordering Data race

17

Actor-based languages (e.g., Erlang, WebWorkers) Functional languages (e.g., Haskell) Sequential programming

slide-18
SLIDE 18

Data races

Sharing Mutation No ordering Data race

18

Rust: No sharing and
 mutation at the same time.

slide-19
SLIDE 19

19

Ownership and Borrowing

Photo Credit: Nathan Kam https://www.youtube.com/watch?v=Tnssn9KcWLg
slide-20
SLIDE 20

Type Ownership Alias? Mutate?

T Owned ✓

~ Ownership and borrowing ~

slide-21
SLIDE 21

fn main() { let apple = Apple::new(); eat(apple); }

21

fn eat(apple: Apple) { … }

Take ownership

  • f the apple

Give ownership

  • f the apple.

Owns Owns

eat(apple);

~~~~~~~~~ Error: `apple` has been moved.

slide-22
SLIDE 22

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

  • f the vector

Give ownership. Give ownership.

Owns Owns

slide-23
SLIDE 23

23

“Manual” memory management in Rust:

Values owned by creator. Values moved via assignment. When final owner returns, value is freed.

Feels invisible.

]

slide-24
SLIDE 24

~ Ownership and borrowing ~

Type Ownership

T

Alias? Mutate?

Owned ✓ &T Shared reference ✓

slide-25
SLIDE 25

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

slide-26
SLIDE 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

slide-27
SLIDE 27

~ Ownership and borrowing ~

Type Ownership

T &T

Alias? Mutate?

Owned Shared reference ✓ ✓ &mut T Mutable reference ✓

slide-28
SLIDE 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(…);

slide-29
SLIDE 29

29

Parallelism

Photo credit: Dave Gingrich https://www.flickr.com/photos/ndanger/2744507570/
slide-30
SLIDE 30

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? Go Code

slide-31
SLIDE 31

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

  • f the data

Error: use of moved value: `book` ~~~~~~~~~~~~~~~~~~~~~~~~~~

slide-32
SLIDE 32

~ Concurrency paradigms ~

Paradigm

Message passing

Ownership? Borrowing?

✓ Fork join ✓

slide-33
SLIDE 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

slide-34
SLIDE 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

slide-35
SLIDE 35

35

Observation:

Building parallel abstractions is easy. Misusing those abstractions is also easy.

slide-36
SLIDE 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”…

+ 1 + 1 1 1 1

slide-37
SLIDE 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) }

~~~~~~~~~❌ ~~~~~~~~~❌

slide-38
SLIDE 38

~ Concurrency paradigms ~

Paradigm

Message passing

Ownership? Borrowing?

✓ Fork join ✓ Lock-free Futures … ✓ ✓ ✓ ✓ Locking ✓ ✓

slide-39
SLIDE 39

39

Unsafe

slide-40
SLIDE 40

40

Ownership and Borrowing P a r a l l e l i s m File System R e f e r e n c e c

  • u

n t

Rust: An Extensible Language

Core Language Libraries

slide-41
SLIDE 41

Safe abstractions

unsafe { … }

Ownership/borrowing/traits give tools to enforce safe abstraction boundaries. Trust me.

fn split_at_mut(…) { }

Validates input, etc.

41

slide-42
SLIDE 42

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%

slide-43
SLIDE 43

43

Photo credit: Don Urban https://www.flickr.com/photos/donpezzano/3044965125/

Integration

slide-44
SLIDE 44

44

usehelix.com

Ruby node.js

neon-bindings/neon PyO3/pyo3 dgrunwald/rust-cpython

CPython

getsentry/milksnake

slide-45
SLIDE 45

45

ArrayBuffer Worker

slide-46
SLIDE 46

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

slide-47
SLIDE 47

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

slide-48
SLIDE 48

48

https://medium.com/@wireapp/3ff37fc98c3f

slide-49
SLIDE 49

49

ruby! { class Console { def log(string: &str) { println!("LOG: {:?}", string); } } }

slide-50
SLIDE 50

50

Rust in Production

slide-51
SLIDE 51

51

Will discuss today! Whitepapers available online.

rust-lang.org/en-US/whitepapers.html

slide-52
SLIDE 52

52

Category: Experienced system devs

slide-53
SLIDE 53

53

slide-54
SLIDE 54

54

slide-55
SLIDE 55

55

Photo credit: Salim Virji https://www.flickr.com/photos/salim/8594532469/

Safety = Eat your spinach!

slide-56
SLIDE 56

56

Gradual adoption works.

slide-57
SLIDE 57

57

Category: Full-stack developers

slide-58
SLIDE 58

58

"Because our product helps people identify why their apps are slow, it is very important that we

  • urselves 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:

slide-59
SLIDE 59

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

  • f 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.

slide-60
SLIDE 60

60

slide-61
SLIDE 61

61

Community

Photo credit: David McSpadden https://www.flickr.com/photos/familyclan/15535822737/
slide-62
SLIDE 62

62

Focus: Productivity and Ergonomics Culmination of 2017 roadmap efforts across the board: documentation, language, tooling, libraries, etc.

slide-63
SLIDE 63

63

RFC Process

slide-64
SLIDE 64

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.

slide-65
SLIDE 65

65

Rust Domain Working Groups

Web Services WebAssembly CLI Apps Embedded Devices

slide-66
SLIDE 66

66

From http://jvns.ca/blog/2016/09/11/rustconf-keynote/

Open and welcoming

slide-67
SLIDE 67

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: