Пример #1
0
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))
Пример #2
0
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")
Пример #3
0
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)
Пример #4
0
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"))
Пример #5
0
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)
Пример #6
0
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))
Пример #7
0
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))
    }
}
""")
Пример #8
0
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)
Пример #9
0
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")