Rust Refresher for Readers
This refresher is about reading, not writing: what a construct tells you when you see it in a review.
Ownership
| You see | It means |
|---|---|
fn f(x: Thing) | f takes ownership. The caller can’t use x afterwards. |
fn f(x: &Thing) | f borrows x read-only. The caller keeps ownership. |
fn f(x: &mut Thing) | f mutates x in place, with exclusive access. |
let y = x.clone() | A copy. Sometimes deliberate, sometimes not (see the Smell Catalog). |
Traits
- A trait is a named capability: “this type can be displayed”, “this type can be iterated”, that kind of thing.
impl Trait for Typesays the type has the capability. A boundT: Traitsays the caller needs it.dyn Traitselects the concrete type at runtime. Generics select it at compile time.
Two questions for any Rust file
- Who owns which data, and for how long? Look at the types: which ones hold data and which ones only reference it.
- What can fail, and where does the failure surface? Look at the
Results. If the design is honest, fallibility is visible in the signatures.
Warm-up
Answer the two questions for this code:
use std::collections::HashMap;
pub struct Inventory {
parts: HashMap<String, u32>,
}
pub struct OutOfStock;
impl Inventory {
pub fn part_count(&self, part: &str) -> Option<u32> {
self.parts.get(part).copied()
}
pub fn take(&mut self, part: &str, n: u32) -> Result<(), OutOfStock> {
match self.parts.get_mut(part) {
Some(count) if *count >= n => {
*count -= n;
Ok(())
}
_ => Err(OutOfStock),
}
}
pub fn into_report(self) -> String {
let mut lines: Vec<String> = self
.parts
.iter()
.map(|(name, count)| format!("{name}: {count}"))
.collect();
lines.sort();
lines.join("\n")
}
}