Step 6 of 6
Putting it together: an inventory
Real programs are built from structs that contain other structs, and from lists of structs. Nothing new is needed: a field can have a struct type, and an array, a Vec or a slice can hold structs.
struct Location {
aisle: u32,
shelf: u32,
}
struct Item {
name: String,
quantity: u32,
location: Location,
}
fn print_low_stock(items: &[Item], threshold: u32) {
for item in items {
if item.quantity < threshold {
println!("restock {} (aisle {}, shelf {})", item.name, item.location.aisle, item.location.shelf);
}
}
}
fn total_units(items: &[Item]) -> u32 {
let mut total = 0;
for item in items {
total += item.quantity;
}
total
}
fn main() {
let items = [
Item { name: String::from("bolts"), quantity: 120, location: Location { aisle: 1, shelf: 2 } },
Item { name: String::from("nuts"), quantity: 8, location: Location { aisle: 1, shelf: 3 } },
Item { name: String::from("hinges"), quantity: 3, location: Location { aisle: 4, shelf: 1 } },
];
print_low_stock(&items, 10);
println!("units in stock: {}", total_units(&items));
}
restock nuts (aisle 1, shelf 3)
restock hinges (aisle 4, shelf 1)
units in stock: 131
item.location.aislechains field access: thelocationfield ofitem, then itsaisle.- The functions take a slice,
&[Item], just like the&[i32]slices from the last module.for item in itemsgives a reference to each item (&Item), so nothing is moved or copied. - Because they take a slice, the same functions work with an array (
&items) or aVec<Item>(&inventory), which coerces to a slice automatically.
You can also grow a list of structs at run time with a Vec:
struct Score {
player: String,
points: u32,
}
fn main() {
let mut scores = Vec::new();
scores.push(Score { player: String::from("Ferris"), points: 40 });
scores.push(Score { player: String::from("Corro"), points: 55 });
for s in &scores {
println!("{}: {}", s.player, s.points);
}
println!("{} players", scores.len());
}
Ferris: 40
Corro: 55
2 players
Designing structs at work
- One struct per concept in your domain:
Customer,Order,Item,Address. If a group of fields always travels together (aisleandshelf), it probably wants its own struct. - Pick field types that rule out nonsense:
u32for a quantity that can't be negative,boolfor yes/no, integer cents for money (neverf64:0.1 + 0.2isn't exactly0.3). - Derive Debug on nearly everything, so you can always
dbg!or log a value.
Your task
Write fn total_value(items: &[Item]) -> u32 that returns the value of the whole stock: for each item, price_cents * quantity, all added up. An empty slice is worth 0.
Challenge 2
Write fn count_low_stock(items: &[Item], threshold: u32) -> usize that counts how many items have a quantity below threshold.
Challenge 3
Write a whole inventory program. Each line of input is an item: a name, a price in cents and a quantity, separated by spaces, like tea 450 3. The starter reads the lines and splits each one into words for you.
For each line, build an Item and push it onto items. After reading everything, print one line per item with its stock value (price_cents * quantity), then the total:
tea: 1350
mug: 1200
pen: 1500
total: 4050