Lecture 26 - Midterm 2 Review

Logistics

  • Final review session in discussion with Joey tomorrow
  • Exam on Wednesday
  • No pre-work for Wednesday OR Friday
  • HW5 due Friday

Quick reference: what you need to know for the exam

This midterm covers Lectures 14-25 (cumulative, but focus on later material):

  1. Stack/Heap (L14): Know rough characteristics of both (fast vs slow, small vs large, ordered vs unordered) Draw memory diagrams showing where String/Vec/Box data lives.
  2. Ownership (L15): Know when data moves vs. copies (simple types on the stack copy, complex types on the heap move, nothing tricky), apply the three ownership rules (don't need to list them)
  3. Borrowing (L16-17): Fill in & or &mut correctly, identify borrow checker compiler errors in simple short programs
  4. Strings and slices (L18): Explain why text[0] fails, what is a slice / fat pointer, difference between &String and &str, what .collect() does
  5. HashMap/HashSet (L19): Explain what is a hash function, advantage of HashSet over Vec, be able to read HashMap methods including .entry().or_insert()
  6. Structs (L20-21): Choose correct self type (&self, &mut self, or self) for methods, destructure structs/enums in match and let bindings, when you should use a struct vs an enum, why you would use a tuple struct
  7. Generics and traits (L22-23): Write generic functions with trait bounds (T: Clone, T: PartialOrd), define traits (distinguish required methods from default implementations), know how to implement them, understand common traits (Debug, Clone, Copy, PartialOrd, PartialEq), know #[derive(--)] is a macro that generates trait implementations
  8. Lifetimes (L24): Know when annotations are needed (multiple reference inputs and reference output), read 'a syntax, know what 'static means
  9. Systems Theory (L25): Give an example of a memory error that is prevented by Rust's ownership rules, know why generics are zero-cost, know string literals live in the binary,
  10. Hand-coding: Implement short functions combining multiple concepts

Problem types:

Similar to last time, but with stack-heap instead of git/shell:

  • Short-answer (12 pts)
  • Fill-ins (9 pts)
  • What does this print (9 pts)
  • Bug-fixing (8 pts)
  • Two stack-heap diagrams (20 pts)
  • Two hand-coding problems (22 pts) = 80 points total

Please look through the exam before you start and make a plan so you don't run out of time!

Let's summarize again

These slides list covers the types of questions and topics that are fair game for Midterm 2.

The exam will focus on material from Lectures 14-25, though it is cumulative so earlier material may also appear.

1. Stack and heap memory (Lecture 14)

You should be able to:

  • Draw stack and heap diagrams showing where data lives
  • Show how String, Vec, and Box store data (pointer + metadata on stack, actual data on heap)
  • Draw multiple stack frames for function calls
  • Indicate what happens when functions return (stack frames disappear)

You DON'T need to: Work with memory addresses, know precise heap allocation algorithms (eg Vec resizing)

Sample Problem: Draw the stack and heap when execution is inside the process function.

fn main() {
    let x = 42;
    let name = String::from("Alice");
    let data = vec![1, 2, 3];
    process(&name);
}

fn process(text: &str) {
    println!("{}", text);
}

2. Ownership rules (Lecture 15)

You should be able to:

  • Identify when data moves vs. copies (stack types copy, heap types move)
  • Recognize what types implement Copy (i32, bool, char, tuples of Copy types)
  • Predict when ownership transfers to a function and when you can't use a value afterward
  • Use .clone() to make explicit copies
  • Understand (not recite) the three ownership rules

You DON'T need to: List the three ownership rules verbatim

Sample Problem: Will this compile? Why or why not?

#![allow(unused)]
fn main() {
let s1 = String::from("hello");
let s2 = s1;
let s3 = s1.clone();
println!("{}", s1);
}

3. Borrowing and references (Lectures 16-17)

You should be able to:

  • Fill in & or &mut correctly in function signatures and calls
  • Apply borrowing rules: many immutable borrows OR one mutable borrow (not both)
  • Use * to dereference when modifying through &mut

You DON'T need to: Know when auto-dereferencing happens or doesn't

Sample Problem: Fill in the blanks to make this code work.

fn update_scores(scores: _____, bonus: i32) {
    for score in scores.iter_mut() {
        *score += bonus;
    }
}

fn main() {
    let mut data = vec![85, 90, 78];
    update_scores(_____, 5);
    println!("{:?}", data);
}

4. Borrow checker errors (Lecture 17)

You should be able to:

  • Identify where borrow checker errors occur in short programs
  • Explain why conflicts between & and &mut cause errors
  • Fix simple borrow checker errors by reordering code

You DON'T need to: Debug complex multi-function borrow checker issues

Sample Problem: Where does the borrow checker error occur and why?

#![allow(unused)]
fn main() {
let mut x = vec![1, 2, 3];
let r1 = &x;
x.push(4);
println!("{:?}", r1);
}

5. Strings and slices (Lecture 18)

You should be able to:

  • Distinguish between String (owned), &str (slice), and &String (reference to String)
  • Explain why text[0] doesn't work (UTF-8 has variable-length characters)
  • Understand what a slice is (fat pointer: pointer + length)
  • Recognize .collect() can build strings from iterators

You DON'T need to: Implement complex string parsing, understand UTF-8 byte encoding details

Sample Problem: Explain why this code panics:

#![allow(unused)]
fn main() {
let text = "🦀Hello";
let slice = &text[0..2];
}

6. HashMap and HashSet (Lecture 19)

You should be able to:

  • Explain what a hash function is and its characteristics (deterministic, fast, hard to invert, uniform spread)
  • Choose HashMap for key-value pairs, HashSet for unique values only
  • Understand .get() returns Option<&V> (key might not exist)
  • Read code using .entry().or_insert() (insert if key missing)
  • Recognize that inserting moves values into the collection

You DON'T need to: Implement hash functions, explain how hashmaps use hashes, memorize HashMap/HashSet method syntax

Sample Problem: What does this print and why?

#![allow(unused)]
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 90);
scores.insert("Bob", 85);
scores.insert("Alice", 95);
println!("{:?}", scores.get("Alice"));
}

7. Structs and methods (Lecture 20-21)

You should be able to:

  • Define structs with named fields
  • Implement methods in impl blocks
  • Choose &self (read), &mut self (modify), or self (consume)
  • Destructure structs and enums with match and let
  • Explain when to use struct vs. enum vs. tuple struct

You DON'T need to: Complex destructuring, .. default syntax

Sample Problem: Complete this implementation by filling in the input parameters appropriately.

#![allow(unused)]
fn main() {
struct Counter {
    value: i32,
}

impl Counter {
    fn new(___) -> Counter { /* ... */ }
    fn increment(___) { /* ... */ }
    fn get_value(___) -> i32 { /* ... */ }
}
}

8. Generics (Lecture 22)

You should be able to:

  • Write generic functions using <T> syntax
  • Add trait bounds like T: PartialOrd or T: Clone
  • Use multiple bounds with + (e.g., T: Clone + PartialOrd)
  • Understand monomorphization (compiler generates separate code for each type)

You DON'T need to: Complex where clause syntax, lifetime bounds with generics

Sample Problem: Write a generic function that finds the minimum of two values and requires the type to be comparable.

9. Traits (Lecture 23)

You should be able to:

  • Define traits with method signatures
  • Implement traits for your own types
  • Use traits as bounds (T: Debug, a: &impl Display)
  • Recognize common traits: Debug, Clone, Copy, PartialEq, PartialOrd
  • Use #[derive(Debug, Clone)] to auto-generate implementations
  • Distinguish required methods from default implementations

You DON'T need to: Trait objects (dyn Trait), advanced trait features like associated types

Sample Problem: Define a trait Summarizable with a method summary(&self) -> String and implement it for a Book struct.

10. Lifetimes (Lecture 24)

You should be able to:

  • Explain why lifetimes exist (prevent dangling references)
  • Read lifetime syntax like 'a in function signatures
  • Identify when lifetime annotations are needed (multiple reference inputs, reference output)
  • Use &'static str for returning string literals, and know 'static means "lives for entire program" and applies to string literals and constants

You DON'T need to: Write complex lifetime annotations, deal with lifetimes in traits or structs

Sample Problem: Which function needs explicit lifetime annotations and why?

#![allow(unused)]
fn main() {
fn first_word(text: &str) -> &str { /* ... */ }
fn longest(x: &str, y: &str) -> &str { /* ... */ }
}

11. Systems and theory (Lecture 25 + concepts throughout)

You should be able to:

  • Name memory safety bugs Rust prevents (use-after-free, double-free, dangling pointers, data races)
  • Give an example of how Rust prevents a specific memory bug (ownership prevents use-after-free, lifetimes prevent dangling references, etc.)
  • Explain "zero-cost abstraction" (high-level features compile to fast low-level code)

You DON'T need to: Interpret C code, analyze glitch videos

Sample Problem: Explain how Rust's ownership system prevents use-after-free bugs.

Activity - Ask and Answer

Phase 1: Question Writing

  • Tear off the last page of your notes from today
  • Pick a codename (favorite Pokémon, secret agent name, whatever) - remember it!

Write one or two of of:

  • A concept you don't fully understand ("I'm confused about...")
  • A study strategy question ("What's the best way to review...")
  • A practice test question (your own or one from these slides)
  • Anything else you'd like to ask your peers ahead of the midterm

Phase 2: Round Robin Answering

  • Pass papers around a few times
  • Read the question, write a helpful response (2-3 min)
  • Repeat 4-5 times (I'll let you know when)

You can answer questions, explain concepts, give tips / encouragement, draw diagrams, wish each other luck

Phase 3: Return & Review

  • Submit on gradescope what codename you chose for yourself
  • Return the papers at the end of class
  • I'll scan and post all papers - you can see the responses you got and also all others