def message_passing(slides: Slides): slide = slides.new_slide() slide.update_style("code", s(size=38)) content = slide_header(slide, "Message passing") code_width = 900 code_step(content.box(width=code_width, height=300), """ let (tx, rx) = mpsc::channel(); thread::spawn(move || { tx.send(5); }); let received = rx.recv(); """, "1", ( (" mpsc::channel();", None, None, None, None), (0, None, None, None, None), (0, 1, 2, 3, None), (0, 1, 2, 3, 4) ), width=code_width) content.box(height=20) content.box(show="next+").text("""Splitting a channel into a receiver + sender removes aliasing and allows moving the sender independently of the receiver.""", s(size=34))
def cpp_alias_mutate(slides: Slides): slide = slides.new_slide() slide.update_style("default", s(size=50, bold=True)) content = slide_header(slide, "Rust's insight") content.box(show="next+").text("""Memory errors arise when aliasing is combined with mutability""") slide = slides.new_slide() slide.update_style("code", s(size=32)) slide.set_style("gray", slide.get_style("code").compose(s(color="#BBBBBB"))) content = slide_header(slide, "C++ UB example") style_green = s(size=40, color="green") style_red = s(size=style_green.size, color="red") header = content.box(width=500, height=50, horizontal=True) header.overlay(show="2").text("Aliasing ✓", style=style_green) header.overlay(show="3").text("Mutability ✓", style=style_green) header.overlay(show="4+").text("Aliasing & Mutability", style=style_red) header.box(show="4+", width=100, x=450).image("imgs/boom.svg") content.box(height=20) wrapper = code_step(content.box(width=700, height=200), """ std::vector<int> vec = { 1, 2, 3 }; int& p = vec[0]; vec.push_back(4); std::cout << p << std::endl; """, 1, [(0, None, None, None), (0, 1, None, 3), (0, None, 2, None), (0, 1, 2, 3)], language="cpp") wrapper.line_box(2, show="4+", z_level=99, width=320).rect(bg_color=CODE_HIGHLIGHT_COLOR) wrapper.line_box(3, show="4+", z_level=99, x=235, width=45).rect(bg_color=CODE_HIGHLIGHT_COLOR) slide = slides.new_slide() content = slide_header(slide, "What to do?") content.box(width=1000).image("imgs/meme-rust-aliasing.jpg") slide = slides.new_slide() content = slide_header(slide, "Rust's solution") large = s(size=50, bold=True) row = content.box(horizontal=True) row.box().text("You can mutate", style=large) row.box(width=20) row.box(show="next+").text("||", style=large) row.box(width=20) row.box(show="next+").text("alias", style=large) content.box(height=10) content.box(show="next+").text( "But not both at the same time (w.r.t. a single variable)") content.box(height=10) content.box(show="next+").text( "Rust enforces this at compile time using its type system")
def lifetimes(slides: Slides): def cpp_lifetime(comment=""): slide = slides.new_slide() slide.update_style("code", s(size=38)) content = slide_header(slide, "Lifetimes (C++)") code(content, """ int* p; {{ int value = 5; p = &value; }}{comment} std::cout << *p << std::endl; """.format(comment=comment), "cpp", width=800) cpp_lifetime() cpp_lifetime(" // <-- `value` is destroyed here") slide = slides.new_slide() slide.update_style("code", s(size=38)) content = slide_header(slide, "Lifetimes (Rust)") code_box = code(content.box(), """ let p; { let value = 5; p = &value; } println!("{}", *p);""", width=800) def line(start, end, start_x, end_x, right_x, offset_x, color, show, y="65%"): line_start = code_box.line_box(start, width="fill") line_end = code_box.line_box(end, width="fill") arrow = Arrow(20) start = line_start.p(start_x, y) end = line_end.p(end_x, y) content.box(show=show).line([ start, line_start.p(right_x, y).add(offset_x, 0), line_end.p(right_x, y).add(offset_x, 0), end ], start_arrow=arrow, end_arrow=arrow, stroke_width=5, color=color) content.box(height=20) content.box(width=500, show="2+").text("Lifetime of reference `p`", style=s(color="orange", align="left")) line(0, 5, "40%", "100%", "90%", 200, "orange", "2+") content.box(height=10) content.box(width=500, show="3+").text("Lifetime of `value`", style=s(color="green", align="left")) line(2, 4, "100%", "10%", "100%", 50, "green", "3+") content.box(height=30) content.box(show="4+").text("""Lifetime of a value must be >= lifetime of a reference to it""", style=s(size=50)) slide = slides.new_slide() slide.update_style("code", s(size=38)) content = slide_header(slide, "Lifetimes (Rust)") code_box = code(content.box(), """ let p; { let value = 5; p = &value; } println!("{}", *p);""", width=800) content.box(height=20) content.box(height=280).image("imgs/lifetime-error.png") slide = slides.new_slide() slide.update_style("code", s(size=32)) content = slide_header(slide, "What if compile time is not enough?") content.box().text("""If you can't prove to the compiler that the lifetimes are correct, lifetime can be managed at runtime.""") code_width = 1000 content.box(height=20) code_step(content.box(width=code_width, height=400), """ fn main() { let value = Rc::new(5); // refcount == 1 { let a = value.clone(); // refcount == 2 } // refcount == 1 } // refcount == 0, value is dropped """, "2", ((0, 1, None, None, None, None), (0, 1, 2, 3, None, None), (0, 1, 2, 3, 4, None), (0, 1, 2, 3, 4, 5)), width=code_width)
def borrowing(slides: Slides): slide = slides.new_slide() content = slide_header(slide, "Where's the aliasing?") content.box().text("So far, we only have mutability, there's no aliasing:") content.box(height=20) list = content.box() list_item(list, show="next+").text( "After a move, the original value is not accessible") list_item(list, show="next+").text("After a copy, a new value is created") slide = slides.new_slide() content = slide_header(slide, "Borrowing") content.box().text( "Aliasing happens when you create a reference to a value") content.box(show="next+").text("This is called borrowing in Rust") slide = slides.new_slide() slide.update_style("code", s(size=38)) content = slide_header(slide, "Shared borrows") code_width = 800 code(content, """ let value = Bitmap::load(...); let a = &value; let b = &value; """, width=code_width) content.box(height=20) list = content.box() list_item( list, show="next+").text("Multiple shared borrows of a value may exist") list.box(height=10) list_item(list, show="next+").text("You can't mutate using a shared borrow") list.box(height=10) code(list.box(show="last+"), "a.width = 10; // does not compile", width=code_width) list.box(height=10) list_item(list, show="next+").text("You can't move out of a shared borrow") list.box(height=10) code(list.box(show="last+"), """ fn foo(bitmap: Bitmap) { } foo(a); // does not compile""", width=code_width) slide = slides.new_slide() slide.update_style("code", s(size=38)) content = slide_header(slide, "Unique borrows") code_width = 800 code(content, """ let value = Bitmap::load(...); let c = &mut value; """, width=code_width) content.box(height=20) list = content.box() list_item(list, show="next+").text( """If a unique borrow exists, there are no other references to the same value""", style=s(align="left")) list_item(list, show="next+").text( "You can only create a unique borrow if you own the value") list.box(height=10) list_item(list, show="next+").text("You can mutate using a unique borrow") list.box(height=10) code(list.box(show="last+"), "c.width = 10;", width=code_width) list.box(height=10) list_item(list, show="next+").text("You can't move out of a unique borrow") list.box(height=10) code(list.box(show="last+"), """ fn foo(bitmap: Bitmap) { } foo(c); // does not compile""", width=code_width) slide = slides.new_slide() slide.update_style("code", s(size=38)) content = slide_header(slide, "Vector example (Rust)") code_width = 800 code(content.box(show="3+"), """ // Vec::push fn push(&mut self, value: T) """, width=code_width) content.box(height=10) code_step(content.box(width=code_width, height=300), """ let vec = vec!(1, 2, 3); let p = &vec[0]; vec.push(4); println!("{}", p); """, "1", ((0, None, None, None), (0, 1, None, None), (0, 1, 2, None), (0, 1, 2, 3)), width=code_width) content.box(height=10) content.box(height=220, show="next+").image("imgs/borrowck-error.png") slide = slides.new_slide() slide.update_style("code", s(size=34)) content = slide_header(slide, "What if compile time is not enough?") content.box().text( """If you can't prove to the compiler that your borrows are safe, borrow checking can be done at runtime.""") content.box( show="next+").text("If any rules are broken, the program panics.") content.box(height=20) code_box = code( content.box(show="next+"), """ let value = RefCell::new(5); let a = value.borrow(); // shared borrow let b = value.borrow_mut(); // unique borrow""") pointer_to_line(slide, code_box, 2, 100, 600, "4+", textbox_pos=("40%", 0), code_pos=("40%", "100%")).text( """This would panic, since there already is a shared borrow""", style=s(color="orange", align="left"))
def shared_state(slides: Slides): slide = slides.new_slide() slide.update_style("code", s(size=38)) content = slide_header(slide, "Spawning a thread") code(content.box(), "fn spawn<F: Fn + Send>(f: F)") content.box(height=20) content.box(show="next+").text("""Ownership of T can be transferred to another thread only if T implements the ~emph{Send} trait""") content.box(height=20) content.box(show="next+").text("""Send is implemented automatically, unless the type contains values that are not safe to be transferred between threads""", style=s(size=30)) slide = slides.new_slide() slide.update_style("code", s(size=34)) content = slide_header(slide, "Shared state concurrency") content.box().text("Goal:", style=s(bold=True)) list = content.box() list_item(list, show="next+").text("Spawn a thread") list_item(list, show="next+").text("Send a reference to some value to it") list_item(list, show="next+").text("Modify the value in the spawned thread") list_item(list, show="next+").text("Read the value in the original thread") slide = slides.new_slide() slide.update_style("code", s(size=34)) (content, header) = slide_header(slide, "Shared state concurrency", True) box = header.box(width=160, y=80) box.image("imgs/meme-face-1.jpg") box.overlay(show="4").image("imgs/meme-face-2.jpg") code_step(content.box(width=800, height=350), """ let value = 5; let p = &value; thread::spawn(|| { println!("{}", *p); }); """, 1, [(0, None, None, None, None), (0, 1, None, None, None), (0, 1, 2, 3, 4)], width=500) content.box(height=10) with_border(content, show="4+").box(height=220).image("imgs/concurrent-error-1.png") slide = slides.new_slide() slide.update_style("code", s(size=34)) (content, header) = slide_header(slide, "Shared state concurrency", True) box = header.box(width=160, y=80) box.image("imgs/meme-face-2.jpg") box.overlay(show="4-5").image("imgs/meme-face-3.png") box.overlay(show="6+").image("imgs/meme-face-4.png") code_step(content.box(width=800, height=350), """ let p = Rc::new(5); thread::spawn(|| { println!("{}", *p); }); """, 1, [(0, None, None, None), (0, 1, None, None), (0, 1, 2, 3), (0, 1, 2, 3), (0, "thread::spawn(move || {", 2, 3)], width=500) border_box = content.box(width=1000, height=220) box = with_border(border_box.overlay(), show="4").box(width=800, height=180) box.box(show="4", height=220).image("imgs/concurrent-error-2.png") box = with_border(border_box.overlay(), show="6+").box(width=800, height=180) box.box(show="6+", width=900).image("imgs/concurrent-error-3.png") slide = slides.new_slide() slide.update_style("code", s(size=34)) (content, header) = slide_header(slide, "Shared state concurrency", True) box = header.box(width=160, y=80) box.overlay(show="1-2").image("imgs/meme-face-6.jpg") box.overlay(show="3+").image("imgs/meme-face-5.jpg") content.box(height=60) code_step(content.box(width=800, height=260), """ let p = Arc::new(5); thread::spawn(move || { println!("{}", *p); }); println!("{}", *p); """, 1, [(0, 1, 2, 3, None), (0, 1, 2, 3, 4), ], width=500) with_border(content, show="3+").box(width=1000).image( "imgs/concurrent-error-4.png") slide = slides.new_slide() slide.update_style("code", s(size=34)) (content, header) = slide_header(slide, "Shared state concurrency", True) box = header.box(width=160, y=80, show="3+") box.image("imgs/meme-face-7.png") code_width = 800 code(content.box(show="2+"), "fn clone(&self) -> Arc<T>;", width=code_width) content.box(height=20) code(content.box(), """ let p = Arc::new(5); let tp = p.clone(); thread::spawn(move || { println!("{}", *tp); }); println!("{}", *p); """, width=code_width) content.box(height=10) content.box(show="2+").text("""Clone() creates a new Arc. Multiple variables remove aliasing.""") content.box(show="3+").text("Arc only provides ~emph{read-only} access (shared borrow).") slide = slides.new_slide() slide.update_style("code", s(size=34)) slide.set_style("code2", slide.get_style("code").compose(s(size=32))) content = slide_header(slide, "Shared state concurrency") code_width = 800 code(content.box(show="4+"), """ // Mutex::lock fn lock(&self) -> &mut T;""", code_style="code2", width=code_width) content.box(height=20) code_step(content.box(width=code_width, height=320), """ let p = Arc::new(Mutex::new(5)); let tp = p.clone(); thread::spawn(move || { *tp.lock() = 10; }); println!("{}", *p.lock());""", "1", ( (" Mutex::new(5) ", None, None, None, None, None), (0, None, None, None, None, None), (0, 1, 2, 3, 4, 5) ), width=code_width)
def unsafe(slides: Slides): slide = slides.new_slide() slide.update_style("code", s(size=32)) content = slide_header(slide, "Where's the catch?") content.box().text("We have seen things that mutate through a shared borrow") code_width = 800 code_step(content.box(width=code_width, height=400), """ // Arc::clone fn clone(&self) -> Arc<T>; // Mutex::lock fn lock(&self) -> &mut T; // AtomicU64::store fn store(&self, val: u64, order: Ordering); """, 2, [ (0, 1, None, None, None, None), (0, 1, 2, 3, None, None), (0, 1, 2, 3, 4, 5) ], width=code_width) content.box(show="5+").text("This is called ~tt{interior mutability} and requires unsafe Rust", s(size=32)) slide = slides.new_slide() content = slide_header(slide, "Enter unsafe Rust") content.box().text("Some scenarios are not expressible in (safe) Rust") content.box(height=20) content.box(show="next+").text("In some cases, something more is required to:") list = content.box() list_item(list, show="next+").text("Express inherently unsafe paradigms") list_item(list, show="next+").text("Improve performance") list_item(list, show="next+").text("Interact with I/O, OS, hardware, network") slide = slides.new_slide() content = slide_header(slide, "Unsafe Rust") content.box().text("You can mark parts of code with the ~tt{unsafe} keyword") content.box(show="next+").text("Unsafe Rust is a ~emph{superset} of Rust") def unsafe_slide(header, code_body, content_show="1", code_size=36): slide = slides.new_slide() slide.update_style("code", s(size=code_size)) content = slide_header(slide, "Unsafe Rust") content.box(y=0).text("Unsafe Rust allows:") content.box(height=20) content.box(show=content_show).text(header) content.box(height=10) code(content.box(show=content_show), code_body) unsafe_slide("Accessing a global mutable variable", """ static mut COUNTER: u32 = 0; fn increment_count() { unsafe { COUNTER += 1; } }""", content_show="2+") unsafe_slide("Dereferencing a raw pointer", """ let ptr = 0xCAFECAFE as *mut u32; unsafe { *ptr = 5; }""") unsafe_slide("Calling an unsafe function", """ unsafe { zlib_compress(&buffer, buffer.len()); }""") unsafe_slide("Implementing an unsafe trait", """ unsafe impl Send for MySuperSafeType { ... }""") slide = slides.new_slide() slide.update_style("code", s(size=18)) content = slide_header(slide, "Finding unsafe code - C++") code_box = code(content, """ std::atomic<LifecycleId> ArenaImpl::lifecycle_id_generator_; GOOGLE_THREAD_LOCAL ArenaImpl::ThreadCache ArenaImpl::thread_cache_ = {-1, NULL}; void ArenaImpl::Init() { lifecycle_id_ = lifecycle_id_generator_.fetch_add(1, std::memory_order_relaxed); hint_.store(nullptr, std::memory_order_relaxed); threads_.store(nullptr, std::memory_order_relaxed); if (initial_block_) { // Thread which calls Init() owns the first block. This allows the // single-threaded case to allocate on the first block without having to // perform atomic operations. new (initial_block_) Block(options_.initial_block_size, NULL); SerialArena* serial = SerialArena::New(initial_block_, &thread_cache(), this); serial->set_next(NULL); threads_.store(serial, std::memory_order_relaxed); space_allocated_.store(options_.initial_block_size, std::memory_order_relaxed); CacheSerialArena(serial); } else { space_allocated_.store(0, std::memory_order_relaxed); } } """) code_box.overlay(show="2+", z_level=99).rect(bg_color=CODE_HIGHLIGHT_COLOR) slide = slides.new_slide() content = slide_header(slide, "Finding unsafe code - Rust") bash(content.box(), '$ grep "unsafe" main.rs', text_style=s(size=40)) slide = slides.new_slide() slide.box().text("""Rust builds safe abstractions on top of unsafe foundations""", s(size=50))
def qol(slides: Slides): slide = slides.new_slide() slide.update_style("code", s(size=50)) content = slide_header(slide, "Type inference") code_width = 900 code_step(content.box(width=code_width, height=400), """ let elem = 5u8; let mut vec = Vec::new(); vec.push(elem); // vec is now Vec<u8> """, "1", ((0, None, None, None), (0, 1, None, None), (0, 1, 2, None), (0, 1, 2, 3)), width=code_width) slide = slides.new_slide() slide.update_style("code", s(size=50)) content = slide_header(slide, "Iterators") code_step(content.box(width=code_width, height=400), """ vec.iter() .zip(iter2) .filter(|(a, b)| a > b) .map(|(a, b)| a * b) .sum::<i32>(); """, "1", ((0, None, None, None, None), (0, 1, None, None, None), (0, 1, 2, None, None), (0, 1, 2, 3, None), (0, 1, 2, 3, 4)), width=code_width) slide = slides.new_slide() slide.update_style("code", s(size=32)) content = slide_header(slide, "Generators") code( content.box(), """ let mut fibonacci = || { yield 1; let mut a = 0; let mut b = 1; loop { yield a + b; a = b; b = a + b; } }; let f = fibonacci().iter().take(5).collect(); """) slide = slides.new_slide() slide.update_style("code", s(size=26)) content = slide_header(slide, "Async/await") code( content.box(), """ async fn compute_job(job: Job) -> Result<Data, Error> { let worker = await!(query_broker()); match worker { Some(worker) => await!(send_job(worker)), None => await!(process_job_locally(job)) } } """)
def macros(slides: Slides): slide = slides.new_slide() slide.update_style("code", s(size=22)) content = slide_header(slide, "Macros") code_width = 920 code_step(content.box(width=code_width, height=200), """ macro_rules! find_min { ($x:expr) => ($x); ($x:expr, $($y:expr),+) => ( std::cmp::min($x, find_min!($($y),+)) ) } """, "1", ((0, 1, None, None, None, 5), (0, 1, 2, 3, 4, 5)), width=code_width) code(content.box(show="next+"), """ find_min!(5); // 5 find_min!(2, 1, 3); // 1 """, width=code_width) content.box(height=20) code(content.box(show="next+"), """ macro_rules! create_function { ($func_name:ident) => ( fn $func_name() { println!("You called {:?}()", stringify!($func_name)); } ) }""", width=code_width) slide = slides.new_slide() slide.update_style("code", s(size=22)) content = slide_header(slide, "Procedural macros") code_width = 920 code(content.box(), """ fn my_macro(attr: TokenStream, item: TokenStream) -> TokenStream { ... }""", width=code_width) code(content.box(show="next+"), """ #[derive(my_macro)] struct Record { #[my_macro] pub id: u32 } """, width=code_width) slide = slides.new_slide() slide.update_style("code", s(size=26)) content = slide_header(slide, "Procedural macros") code_width = 700 code(content.box(), """ #[derive(Serialize, Deserialize)] struct Person { name: String, age: u32 }""", width=code_width) code(content.box(show="next+"), """ json::to_string(person); yaml::to_string(person); let person = json::parse(person_str);""", width=code_width) content.box(height=10) code(content.box(show="next+"), """ #[derive(CmdArgs)] struct Args { #[arg(short = "d", long = "debug")] debug: bool, #[arg(parse(from_os_str))] path: PathBuf, } """, width=code_width) content.box(height=10) code(content.box(show="next+"), """ #[derive(Debug)] struct Person { name: String, age: u32 } println!("{:?}", person); """, width=code_width)
def project(slides: Slides): slide = slides.new_slide() content = slide_header(slide, "Project management (Cargo)") content.box(height=400).image("imgs/cargo.png") slide = slides.new_slide() slide.update_style("code", s(size=30)) content = slide_header(slide, "Using libraries") line = content.box(width="fill", horizontal=True) cargo = line.box(width="50%", p_right=50) cargo.box().text("Cargo.toml") cargo_code = code( cargo.box(), """ [package] name = "hello_world" version = "0.1.0" [dependencies] ibverbs = "0.4" json = "1.0" protobuf = "2.0" """, "toml") main = line.box(width="50%", show="2+", y=0) main.box().text("main.rs") main_code = code( main.box(), """ use json::parse; fn main() { parse("data.json"); }""") arrow = Arrow(20) p1 = cargo_code.line_box(5).p("100%", "50%") p2 = main_code.line_box(0).p(0, "50%") slide.box(show="2+").line( [p1.add(-40, 0), p1, p2.add(-10, 0)], stroke_width=5, color="orange", end_arrow=arrow) content.box(height=10) content.box(show="3+").text("More than 26k libraries available") slide = slides.new_slide() content = slide_header(slide, "Unified documentation") content.box(width=900).image("imgs/rust-docs.png") slide = slides.new_slide() content = slide_header(slide, "Integrated tooling") def cargo_line(parent, text, code, show="1+", **text_args): wrapper = parent.box(width="fill", horizontal=True, show=show) textbox = wrapper.box(width="50%") textbox = textbox.text(text, **text_args) codebox = wrapper.box(width="40%") bash(codebox, code, x=0, width="fill") return textbox wrapper = content.box(width="fill") cargo_line(wrapper, "Build", "$ cargo build", "1+") cargo_line(wrapper, "Run", "$ cargo run", "2+") slide = slides.new_slide() slide.update_style("code", s(size=40)) content = slide_header(slide, "Integrated tooling (tests)") code(content.box(), """ #[test] fn test_add() { assert_eq!(add(1, 2), 3); } """) content.box(height=20) bash(content.box(show="2+"), "$ cargo test") slide = slides.new_slide() slide.update_style("code", s(size=40)) content = slide_header(slide, "Integrated tooling (benchmarks)") code( content.box(), """ #[bench] fn bench_add_two(b: &mut Bencher) { b.iter(|| add_two(2)); } """) content.box(height=20) bash(content.box(show="2+"), "$ cargo bench") slide = slides.new_slide() content = slide_header(slide, "Integrated tooling") wrapper = content.box(width="fill") cargo_line(wrapper, "Format", "$ cargo fmt") cargo_line(wrapper, "Lint", "$ cargo clippy", "next+") box = cargo_line(wrapper, "Publish to SC", "$ cargo publish", "next+") box = box.line_box(0) box.line([box.p("53%", "55%"), box.p("73%", "55%")], stroke_width=3) slide = slides.new_slide() slide.update_style("code", s(size=24)) content = slide_header(slide, "Build scripts") content.box().text("build.rs") code_width = 940 code_step(content.box(width=code_width, height=400), """ fn main() { // generate Protobuf objects protoc_rust::run("protobuf/message.proto", "src/protos"); // generate C headers cbindgen::Builder::new() .generate() .write_to_file("bindings.h"); } """, "1", ((0, 1, 2, 3, None, None, None, None, 8), (0, 1, 2, 3, 4, 5, 6, 7, 8)), width=code_width) slide = slides.new_slide() content = slide_header(slide, "(interlude)") content.box(height=600).image("imgs/meme-cargo.jpg")