Structs
Struct Update Syntax
Struct update syntax (..base_instance) creates a new struct instance by specifying only the fields that should change and copying or moving the remaining unspecified fields from an existing instance of the same struct.
Syntax and Placement Rule (..base Must Be Last)
Inside a struct literal StructName { ... }, place ..existing_struct at the very end of the field list (with no trailing comma after ..existing_struct):
#[derive(Debug)]
struct AppSettings {
theme: String,
font_size: u16,
line_numbers: bool,
auto_save: bool,
}
fn main() {
let default_settings = AppSettings {
theme: String::from("dark"),
font_size: 14,
line_numbers: true,
auto_save: true,
};
// Create a new `AppSettings` overriding only `font_size`, taking the rest from `default_settings`:
let presentation_mode = AppSettings {
font_size: 20,
..default_settings
};
println!("Presentation mode: {presentation_mode:?}");
}
Presentation mode: AppSettings { theme: "dark", font_size: 20, line_numbers: true, auto_save: true }
Ownership and Partial Moves in ..base
Struct update syntax uses = assignment semantics for every field it takes from the base struct:
- If all fields taken from
..baseimplementCopy, the base struct remains completely valid after the update! - If any field taken from
..basedoes NOT implementCopy(such asString), that field is moved into the new struct, rendering the base struct partially moved!
Case 1 — Base has `theme: String`, and we override ONLY `font_size`:
`theme` is taken from `default_settings` --> `default_settings.theme` is MOVED!
Case 2 — Base has `theme: String`, and we override `theme` with a NEW `String`:
Only `Copy` fields (`font_size`, `line_numbers`, `auto_save`) are taken from `default_settings`
--> `default_settings` remains 100% VALID and usable!
#[derive(Debug)]
struct Profile {
username: String,
level: u32,
active: bool,
}
fn main() {
let user_1 = Profile {
username: String::from("alice"),
level: 5,
active: true,
};
// Override the ONLY non-Copy field (`username`), so `..user_1` copies only `level` and `active`!
let user_2 = Profile {
username: String::from("bob"),
..user_1
};
// Because `user_1.username` was NOT moved by `..user_1`, `user_1` is still completely valid!
println!("user_1 (still valid!): {user_1:?}");
println!("user_2: {user_2:?}");
}
user_1 (still valid!): Profile { username: "alice", level: 5, active: true }
user_2: Profile { username: "bob", level: 5, active: true }
Combining Struct Update Syntax with Default::default()
When a struct implements Default, writing ..Default::default() at the end of a struct literal lets you initialize just one or two fields and fill all remaining fields with their default values:
#[derive(Debug, Default)]
struct QueryOptions {
limit: usize,
offset: usize,
include_archived: bool,
}
fn main() {
let opts = QueryOptions {
limit: 25,
..Default::default()
};
println!("{opts:?}");
}
QueryOptions { limit: 25, offset: 0, include_archived: false }