Considering Rust February 2020 Jon Gjengset Graduate student at - - PowerPoint PPT Presentation

considering rust
SMART_READER_LITE
LIVE PREVIEW

Considering Rust February 2020 Jon Gjengset Graduate student at - - PowerPoint PPT Presentation

Considering Rust February 2020 Jon Gjengset Graduate student at MITs Parallel and Distributed Getting in touch: Operating Systems group. - jon@tsp.io Why me? - https://tsp.io - https://twitter.com/jonhoo - Noria: 70k LOC Rust


slide-1
SLIDE 1

Considering Rust

February 2020

slide-2
SLIDE 2

Jon Gjengset

Graduate student at MIT’s Parallel and Distributed Operating Systems group. Why me?

  • Noria: 70k LOC Rust database
  • Contributor to Rust std and async runtime
  • 150+ hours of Rust live-coding streams
  • Experience in C, C++, Go, and Python, as

well as familiarity with Java Getting in touch:

  • jon@tsp.io
  • https://tsp.io
  • https://twitter.com/jonhoo
  • https://github.com/jonhoo
  • https://youtube.com/c/JonGjengset
slide-3
SLIDE 3

Why we’re all here today

Things we will cover:

  • High-level comparisons with

Java, C++, and Python

  • Rust’s major selling points
  • Rust’s primary drawbacks
  • Long-term viability

Things we will not cover:

  • Learning to program in Rust
slide-4
SLIDE 4

First, meet Rust

slide-5
SLIDE 5

The buzzwords

■ By Mozilla, for “systems programming” ■ “Fast, reliable, productive — pick three” ■ “Fearless concurrency” ■ Community-driven and open-source

slide-6
SLIDE 6

The technical bits

■ Compiled language (not bytecode — machine code)

Strong, static typing

Imperative, but with functional aspects

No garbage collection or runtime

Elaborate type system

slide-7
SLIDE 7

Quick comparison

slide-8
SLIDE 8

vs Python

Much faster. Much lower memory use. Multi-threading. Algebraic data types. Pattern matching. Comprehensive static typing, so: Many fewer runtime crashes.

slide-9
SLIDE 9

vs Java

No JVM overhead or GC pauses. Much lower memory use. Zero-cost abstractions. ConcurrentModificationException Pattern matching. Unified build system. Dependency management.

slide-10
SLIDE 10

vs C/C++

No segfaults. No buffer overflows. No null pointers. No data races. Powerful type system. Unified build system. Dependency management.

slide-11
SLIDE 11

vs Go

No GC pauses; lower memory use. No null pointers. Nicer error handling. Safe concurrency. Stronger type system. Zero-cost abstractions. Dependency management.

slide-12
SLIDE 12

Rust’s primary features

slide-13
SLIDE 13

Modern language

  • r: it’s nice to use

Nice and efficient generics. Algebraic data types + patterns. Modern tooling.

slide-14
SLIDE 14

Modern language

  • r: it’s nice to use

Nice and efficient generics. Algebraic data types + patterns. Modern tooling.

slide-15
SLIDE 15

Nice and efficient generics.

struct MyVec<T> { // ... } impl<T> MyVec<T> { pub fn find<P>(&self, predicate: P) -> Option<&T> where P: Fn(&T) -> bool { for v in self { if predicate(v) { return Some(v); } } None } }

slide-16
SLIDE 16

Modern language

Nice and efficient generics. Algebraic data types + patterns. Modern tooling.

slide-17
SLIDE 17

Algebraic data type + patterns

// Option<T> is an enum that is either Some(T) or None if let Some(f) = my_vec.find(|t| t >= 42) { /* found */ }

slide-18
SLIDE 18

Algebraic data type + patterns

// Option<T> is an enum that is either Some(T) or None if let Some(f) = my_vec.find(|t| t >= 42) { /* found */ } enum DecompressionResult { Finished { size: u32 }, InputError(std::io::Error), OutputError(std::io::Error), } // this will not compile: match decompress() { Finished { size } => { /* parsed successfully */ } InputError(e) if e.is_eof() => { /* got EOF */ } OutputError(e) => { /* output failed with error e */ } }

slide-19
SLIDE 19

Modern language

Nice and efficient generics. Algebraic data types + patterns. Modern tooling.

slide-20
SLIDE 20

Modern tooling — built-in testing and docs; friendly errors

#[test] fn it_works() { assert_eq!(1 + 1, 2); } /// Returns one more than its argument. /// /// ``` /// assert_eq!(one_more(42), 43); /// ``` pub fn one_more(n: i32) -> i32 { n + 1 }

slide-21
SLIDE 21

Modern language

Nice and efficient generics. Algebraic data types + patterns. Modern tooling.

slide-22
SLIDE 22

Safety by construction

  • r: it’s harder to misuse

Pointers checked at compile-time. Thread-safety from types. No hidden states.

slide-23
SLIDE 23

Safety by construction

  • r: it’s harder to misuse

Pointers checked at compile-time. Thread-safety from types. No hidden states.

slide-24
SLIDE 24

Pointers checked at compile-time.

// every value has an owner, responsible for destructor (RAII). // compiler checks: // only ever one owner: // no double-free let x = Vec::new(); let y = x; drop(x); // illegal, y is now owner // no pointers live past owner changes or drops: // no dangling pointers/use-after-free let mut x = vec![1, 2, 3]; let first = &x[0]; let y = x; println!(“{}”, *first); // illegal, first became invalid when x was moved

slide-25
SLIDE 25

Pointers checked at compile-time.

let v = Vec::new(); // this compiles just fine: println!(“len: {}”, v.len()); // this will not compile; would need mutable access v.push(42);

slide-26
SLIDE 26

Pointers checked at compile-time.

let v = Vec::new(); accidentally_modify(&v); fn accidentally_modify(v: &Vec<i32>) { // this compiles just fine: println!(“len: {}”, v.len()); // this will not compile; would need &mut Vec<i32> push(v); } // explicitly declare need for mutable access fn push(v: &mut Vec<i32>) { v.push(42); } // this will not compile either: push(&mut v);

slide-27
SLIDE 27

Safety by construction

Pointers checked at compile-time. Thread-safety from types. No hidden states.

slide-28
SLIDE 28

Thread-safety embedded in type system.

use std::cell::Rc; // reference-counted, non atomic use std::sync::Arc; // reference-counted, atomic // this will not compile: let rc = Rc::new(“not thread safe”); std::thread::spawn(move || { println!(“I have an rc with: {}”, rc); }); // this compiles fine: let arc = Arc::new(“thread safe”); std::thread::spawn(move || { println!(“I have an arc with: {}”, arc); }); // this will also not compile: let mut v = Vec::new(); std::thread::spawn(|| { v.push(42); }); let _ = v.pop();

slide-29
SLIDE 29

Safety by construction

Pointers checked at compile-time. Thread-safety from types. No hidden states.

slide-30
SLIDE 30

No hidden states.

enum Option<T> { Some(T), None, } enum Result<T, E> { Ok(T), Err(E), } // v is Option<&T>, not &T -- cannot use without checking for None let v = my_vec.find(|t| t >= 42); // n is Result<i32, ParseIntError> -- cannot use without checking for Err let n = “42”.parse(); // ? suffix is “return Err if Err, otherwise unwrap Ok” let n = “42”.parse()?;

slide-31
SLIDE 31

Safety by construction

Pointers checked at compile-time. Thread-safety from types. No hidden states.

slide-32
SLIDE 32

Low-level control

No GC or runtime. Control allocation and dispatch. Can write + wrap low-level code.

  • r: it gets out of your way
slide-33
SLIDE 33

Low-level control

No GC or runtime. Control allocation and dispatch. Can write + wrap low-level code.

  • r: it gets out of your way
slide-34
SLIDE 34

No GC or runtime. That means: No garbage collection pauses. No memory overhead (except what you add). Can issue system calls (incl. fork/exec). Can run on systems without an OS. Free FFI calls to other languages.

slide-35
SLIDE 35

Low-level control

No GC or runtime. Control allocation and dispatch. Can write + wrap low-level code.

slide-36
SLIDE 36

Control allocation and dispatch.

// variables are all on the stack let x = 42; let z = [0; 1024]; // can opt-in to heap allocation let heap_x = Box::new(x); let heap_z = vec![0; 1024]; // can swap out allocator for performance or on embedded #[global_allocator] static A: MyAllocator = MyAllocator; // can opt-in to dynamic dispatch (vtable): only one copy of find per T impl<T> MyVec<T> { pub fn find(&self, f: &dyn Fn(&T) -> bool) -> Option<&T> { // ...

slide-37
SLIDE 37

Low-level control

No GC or runtime. Control allocation and dispatch. Can write/wrap low-level code.

slide-38
SLIDE 38

Can write and wrap low-level code.

// can do highly unsafe things by marking them as such let vga_ptr = 0xB8000 as *mut u8; // assuming we have exclusive access to VGA memory let vga = unsafe { std::slice::from_raw_parts_mut(vga_ptr, 80 * 24) }; vga[0] = b'X'; // back in safe code, usual rules apply: *vga_ptr = b'Y'; // invalid; trying to dereference raw pointer vga[80 * 24] = b'Y'; // bounds check will catch this

slide-39
SLIDE 39

Low-level control

No GC or runtime. Control allocation and dispatch. Can write + wrap low-level code.

slide-40
SLIDE 40

Compatibility

  • r: it plays nicely with others

Zero-overhead FFI. Great WebAssembly support. Works with traditional tools.

slide-41
SLIDE 41

Compatibility

  • r: it plays nicely with others

Zero-overhead FFI. Great WebAssembly support. Works with traditional tools.

slide-42
SLIDE 42

Zero-overhead FFI.

// trivial to get access to any function following C ABI extern "C" { fn c_abs(input: i32) -> i32; } fn main() { // inherently unsafe; who knows what C code does println!(“{}”, unsafe { c_abs(-42) }); } // trivial to expose Rust functions/types through C ABI #[no_mangle] pub extern "C" fn callable_from_c() { println!("This function is callable from C"); }

slide-43
SLIDE 43

Compatibility

Zero-overhead FFI. Great WebAssembly support. Works with traditional tools.

slide-44
SLIDE 44

Compatibility

Zero-overhead FFI. Great WebAssembly support. Works with traditional tools.

slide-45
SLIDE 45

Works with traditional tools. Rust uses LLVM, normal calling conventions, no runtime, DWARF, so: perf works. gdb/lldb works. valgrind works. LLVM sanitizers work.

slide-46
SLIDE 46

Compatibility

Zero-overhead FFI. Great WebAssembly support. Works with traditional tools.

slide-47
SLIDE 47

Tooling

Dependency management. Standard tools included. Excellent support for macros.

  • r: it comes with batteries
slide-48
SLIDE 48

Tooling

  • r: it comes with batteries

Dependency management. Standard tools included. Excellent support for macros.

slide-49
SLIDE 49

Built-in dependency management.

# Cargo.toml [dependencies] regex = “1.3.3” rayon = “1.2” csv = { git = “https://github.com/…” } [dev-dependencies] quickcheck = “0.9.2” $ cargo build Downloading regex 1.3.4 Downloading rayon 1.3.0 Updating git repository ‘https://github.com/…’ Compiling ... $ cargo test Downloading quickcheck 0.9.2 Compiling ... Running ...

slide-50
SLIDE 50

Tooling

Dependency management. Standard tools included. Excellent support for macros.

slide-51
SLIDE 51

Standard tools included. Standard distribution ships with: cargo fmt — code formatter cargo doc — documentation generator cargo clippy — linter rls/rust-analyzer — compiler front-end for IDE integration

slide-52
SLIDE 52

Tooling

Dependency management. Standard tools included. Excellent support for macros.

slide-53
SLIDE 53

Excellent support for macros.

macro_rules! assert_eq { ($left:expr, $right:expr) => { let left = $left; let right = $right; if left != right { panic!("assertion failed: {:?} != {:?}", left, right); } } } assert_eq!(1 + 1, 2);

slide-54
SLIDE 54

Excellent support for macros.

macro_rules! assert_eq { ($left:expr, $right:expr) => { let left = $left; let right = $right; if left != right { panic!("assertion failed: {:?} != {:?}", left, right); } } } assert_eq!(1 + 1, 2); #[derive(Serialize, Deserialize)] struct Person { name: String, age: u32, }

slide-55
SLIDE 55

Tooling

Dependency management. Standard tools included. Excellent support for macros.

slide-56
SLIDE 56

Asynchronous code

Language support for writing asynchronous code. Choose your own runtime! Watch this space — still evolving.

slide-57
SLIDE 57

Rust’s primary drawbacks

slide-58
SLIDE 58

Learning curve

The borrow checker is different. No object-oriented programming.

slide-59
SLIDE 59

Ecosystem

Young, and few maintainers. Small (but growing quickly).

slide-60
SLIDE 60

No runtime

No runtime reflection. No runtime-aided debugging.

slide-61
SLIDE 61

Compile time

Improving, but still slow. No pre-built libraries.

slide-62
SLIDE 62

Vendor support

Huge C++ libraries are a pain. Tooling that only supports C++.

slide-63
SLIDE 63

Windows

Full compiler/std support. Limited library support.

slide-64
SLIDE 64

Long-term viability

slide-65
SLIDE 65

Long-term viability seems high.

Most loved language four years running.

Adoption by large companies (“Friends of Rust”):

○ Mozilla, Dropbox, CloudFlare, Microsoft, Google, Amazon, Facebook, Atlassian, npm

Great interoperability story; easy incremental adoption.

Increased company involvement in Rust itself.

~10 yearly conferences around the world.

slide-66
SLIDE 66

For more:

  • https://matklad.github.io/2020/02/14/why-rust-is-loved.html
  • https://twitter.com/jonhoo/status/1205184012861497344
  • https://twitter.com/jonhoo/status/1215739214576287744