Skip to content

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

Easy Primitives

Replace a manual default with a derive

Replace the starter's manual Default implementation with #[derive(Default)]. Keep the Settings fields and the behavior of field_defaults unchanged.

pub fn field_defaults(names: Vec<String>) -> Vec<String>

For each input name, return the corresponding text:

Exact name Returned string
retries 0
verbose false
label Two double-quote characters: ""
Anything else ?

Preserve input order and duplicates. Match names exactly, with no trimming or case conversion: Retries and retries both produce ?. Empty input produces an empty vector. Returned strings have no added newline.

The label result is the Debug representation of an empty String. It contains two quote characters; it is not itself empty. The surrounding JSON may escape those quotes when displaying the result.

Why the starter fails grading

The starter already computes the required outputs. Its manual implementation sets retries to zero, verbose to false and label to an empty string. These are the field types' defaults, so Clippy's derivable_impls warning identifies a simpler equivalent implementation. Under the site's -D warnings check, that warning blocks a passing grade even though the output tests pass.

Remove the entire impl Default for Settings block and add Default to the existing derive list. Leaving both implementations would create a conflicting implementation error. No changes to the reporting function are needed.

Derive the field defaults

For a struct, derived Default calls each field type's Default implementation. This complete example uses an integer, a boolean and a string:

#[derive(Default)]
struct Options {
    attempts: u32,
    enabled: bool,
    title: String,
}

fn main() {
    let options = Options::default();
    assert_eq!(options.attempts, 0);
    assert!(!options.enabled);
    assert_eq!(format!("{:?}", options.title), "\"\"");
}

A derive requests generated trait implementations from the type declaration. Standard derives include Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash and Default. Each has its own requirements. Rust provides no built-in Display derive; a procedural macro can provide one when a project chooses an appropriate representation.

Keep a manual implementation when it changes the contract

A default is a design choice. If attempts should start at three rather than zero, the example above needs a custom implementation or a separate constructor. Deriving Default does not validate application settings.

Generic types need another check: the generated bounds may be more restrictive than a manual implementation. For example, deriving Default for struct Stack<T> { items: Vec<T> } requires T: Default, although creating an empty vector does not. A manual implementation can make an empty stack for every T. This does not mean every derive always adds a bound to every parameter: derived Default for an enum with a default unit variant is one counterexample.

Those generic cases differ from this exercise's concrete Settings struct. Here the derive preserves both the intended values and the available API. Compare the actual implementations and compiler diagnostics rather than assuming every manual implementation can be replaced.