Skip to content

← Shape Your Data: Structs, Enums, Pattern Matching step 22 of 26

Easy Primitives

Build a configuration from defaults and overrides

Build a configuration from (key, value) pairs. Start with the defaults, process the pairs in order, and return a summary of the final configuration.

pub fn build_config(pairs: Vec<(String, String)>) -> String

Override rules

Key Default What to do with its value
retries 0 Parse as u16; use 0 if parsing fails.
name empty string Store the string exactly as supplied.
mode Idle fast selects Fast, safe selects Safe, everything else selects Idle.

Match keys and mode values exactly, including case. Do not trim whitespace. Ignore unknown keys. A later recognized key replaces that field's previous value without resetting other fields. In particular, an invalid retries value resets retries to zero, even after a valid override.

Use Rust's str::parse::<u16>() rules: decimal digits with an optional leading +, no surrounding whitespace, and a value from 0 through 65535. Leading zeros are accepted. Empty strings, negative numbers, underscores, hexadecimal prefixes and out-of-range values fail to parse.

Return retries|name|mode, with retries in decimal and mode spelled exactly Idle, Fast or Safe. Add no final newline. Preserve the name unchanged, including Unicode, whitespace, newlines or | characters; this exercise does not escape separators or define a round-trip serialization format.

Examples:

  • No pairs produce 0||Idle.
  • [("name", "alpha"), ("mode", "fast")] produces 0|alpha|Fast.
  • [("retries", "5"), ("retries", "bad")] produces 0||Idle.

What Default supplies

The starter's Cfg derives Default, which calls each field type's default: zero for u16, an empty String, and Mode::Idle as selected by the enum's #[default] attribute. Derived enum defaults require one selected unit variant; a variant carrying data or marked #[non_exhaustive] cannot use this attribute. A manual Default implementation can construct a variant with data if that is the desired default.

Rust functions do not have built-in default parameter values. A configuration struct is one way to offer optional settings; separate functions and builders are other choices. Default chooses a value, not a validation policy. Zero retries and an empty name are valid here because the task defines them that way. Another API may require explicit values or a validating constructor.

Fill unspecified fields with struct update syntax

This complete example changes one setting and obtains the rest from defaults:

#[derive(Default, Debug, PartialEq)]
enum Mode {
    #[default]
    Idle,
    Fast,
}

#[derive(Default)]
struct Settings {
    retries: u16,
    name: String,
    mode: Mode,
}

fn main() {
    let initial = Settings { retries: 5, ..Settings::default() };
    assert_eq!(initial.mode, Mode::Idle);
    let updated = Settings { mode: Mode::Fast, ..initial };
    assert_eq!(updated.retries, 5);
    assert_eq!(updated.name, "");
    assert_eq!(updated.mode, Mode::Fast);
}

..initial supplies only the fields not written explicitly. Copy fields can be copied; other fields can be moved. Here it moves the String into updated, so initial is no longer usable as a whole. The base expression must be last, with no comma after it.

In your loop, use updates such as Cfg { name: value, ..cfg } and assign the resulting configuration back to cfg. The binding still needs mut because it receives a new value on each iteration.

Fix both starter problems

The starter sets retries to 3 and ignores every pair. It must instead keep the initial default of 0 and implement the override rules above. It also triggers Clippy's field_reassign_with_default warning by creating a default value and immediately assigning one field. Struct update syntax expresses an initial override directly; this lint is not a ban on later mutation.

Struct construction also obeys privacy and API restrictions. Outside the defining crate, a #[non_exhaustive] struct cannot be constructed with a literal, including one using update syntax. If you already have a value, accessible public fields can still be mutated. See the Rust reference.