9.3 Updating Struct Instances

9.3.1 Struct Update Syntax

Rust provides a convenient way to create a new struct instance by copying most of the values from another instance. This is called struct update syntax.

let new_instance = StructName {
    field1: new_value1,
    ..old_instance
};
  • The .. syntax copies the remaining fields from old_instance.
  • Field Order: The ..old_instance must be specified last.

Example:

struct Person {
    name: String,
    age: u8,
}
fn main() {
    let person1 = Person {
        name: String::from("Carol"),
        age: 22,
    };
    let person2 = Person {
        name: String::from("Dave"),
        ..person1
    };
    println!("{} is {} years old.", person2.name, person2.age);
}
  • Note: person2 will have name set to "Dave" and age set to 22, copied from person1.

Ownership Considerations

Using ..person1 in struct update syntax moves the values from person1 to person2. After this operation, person1 cannot be used if it contains types that do not implement the Copy trait (such as String).

struct Person {
    name: String,
    age: u8,
}
fn main() {
    let person1 = Person {
        name: String::from("Carol"),
        age: 22,
    };
    let person2 = Person {
        name: String::from("Dave"),
        ..person1
    };
    // println!("Person1's name: {}", person1.name); // Error: borrow of moved value
}
  • Since String does not implement Copy, person1.name has been moved to person2.name, and person1 can no longer be used.

9.3.2 Field Init Shorthand

When the field name and the variable name are the same, you can use shorthand initialization.

let name = String::from("Eve");
let age = 28;

let person = Person { name, age };
  • This is equivalent to:
let person = Person {
    name: name,
    age: age,
};

9.3.3 Using Default Values

If a struct implements the Default trait, you can create a default instance and then override specific fields.

First, derive the Default trait:

#![allow(unused)]
fn main() {
#[derive(Default)]
struct Person {
    name: String,
    age: u8,
}
}

You can create a default instance in two ways:

  1. Using Person::default():

    let person = Person::default();
  2. Using Default::default():

    let person: Person = Default::default();
  • Note: Both methods are equivalent; Person::default() explicitly calls the default function for the Person type, while Default::default() relies on type inference to determine which default function to call.

Creating an Instance with All Default Values

You can create an instance with all fields set to their default values:

let mut anna = Person::default();
  • This creates a Person instance where name is an empty String, and age is 0 (the default value for u8).

Using Default Values in Struct Update Syntax

You can create a new instance by overriding some fields and filling in the rest with default values:

let person = Person {
    name: String::from("Eve"),
    ..Person::default()
};
  • Here, we explicitly call Person::default() to provide the default values for the remaining fields.

When to Use Which

  • Use Person::default() when you want to be explicit about the type.
  • Use Default::default() when the type can be inferred, or when you prefer the more general approach.

9.3.4 Implementing the Default Trait Manually

If you need custom default values or cannot derive Default, you can implement the Default trait manually:

impl Default for Person {
    fn default() -> Self {
        Person {
            name: String::from("Unknown"),
            age: 0,
        }
    }
}

You can then use Person::default() or Default::default() as before.