Step 1 of 6
Grouping data with a struct
So far, related values have lived in separate variables: name, score, level. With three players you'd juggle nine variables, and nothing stops you from mixing up whose score is whose. A tuple like (String, u32, u8) groups them, but player.1 doesn't say what the number means.
A struct (short for structure) is a type you define yourself that groups related values under one name. Each value is a field, with a name and a type.
struct Player {
name: String,
score: u32,
level: u8,
}
fn main() {
let p = Player {
name: String::from("Ferris"),
score: 1200,
level: 7,
};
println!("{} is level {} with {} points", p.name, p.level, p.score);
}
Ferris is level 7 with 1200 points
Piece by piece:
struct Player { ... }defines a new type calledPlayer. It goes outsidefn main, at the top level of the file. A definition is a blueprint: it doesn't create any value by itself.- Inside the braces, each field is written
name: Type, separated by commas. A comma after the last field is normal Rust style (cargo fmtadds it for you). Player { name: ..., score: ..., level: ... }creates an instance: an actual value of typePlayer. You must give every field a value, but the order doesn't have to match the definition.p.namereads a field with a dot, the same way you read.0of a tuple, but with a name that says what it is.
Type names use UpperCamelCase (Player, InventoryItem); field names use snake_case, like variables. The compiler warns you if you mix them up.
Why bother?
A struct gives meaning to data. p.score says what the number is, the type Player can appear in function signatures (fn print_player(p: &Player)), and the compiler guarantees that every Player has all of its fields. Fields can be any type: numbers, bool, String, arrays, tuples, even other structs.
For text fields, use String: the struct then owns its text, just like a String variable does. A &str field would borrow text owned by someone else, which needs lifetimes, a later module. Owned fields are the right default.
Common mistakes
- Leaving out a field. Every field needs a value; there are no automatic defaults:
struct Player {
name: String,
score: u32,
}
fn main() {
let p = Player { name: String::from("Ferris") };
println!("{}", p.name);
}
error[E0063]: missing field `score` in initializer of `Player`
=instead of:when creating an instance:Player { name = ... }givesexpected `:`, found `=`. Fields use colons both in the definition and in the instance.- A semicolon after the definition:
struct Player { ... };givesexpected item, found `;`. A struct definition with braces ends with}alone.
Your task
Fill in the blanks: the type of score (a whole number that can't be negative: u32), the struct's name when creating the instance, and the field printed first. The program should print Ferris has 120 points.
Challenge 2
Fill in the blanks so the program prints The Rust Book (560 pages): the type of the title field, the field name when creating the instance, and the field read last.
Challenge 3
Write it all yourself. Define a struct Ticket with three fields: id (a u32), title (a String) and open (a bool). In main, create a ticket with id 42, title Login button broken and open true, then print it with one println! that reads all three fields:
#42 Login button broken (open: true)