We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Shape Your Data: Structs, Enums, Pattern Matching step 22 of 26
Default, #[default] on an enum variant, and ..Default::default()
Build a configuration from a list of (key, value) pairs, starting from
defaults and applying only the keys that are present.
pub fn build_config(pairs: Vec<(String, String)>) -> String
Recognised keys: retries (a u16), name (a String), mode (one of
fast, safe, anything else meaning Idle). A retries value that does
not parse falls back to the default. Unknown keys are ignored. Later pairs
win.
Render as "{retries}|{name}|{mode:?}". With no pairs at all that is
"0||Idle".
Rust has no default arguments
No optional parameters, no keyword arguments, no overloading. A function with
four configurable knobs cannot be called four different ways. What Rust has
instead is the Default trait plus struct update syntax:
let cfg = Cfg { retries: 5, ..Default::default() };
Two knobs, one line, every other field at its default. This is the lightweight alternative to a full builder, and for most types it is enough.
Derived Default gives every field its default: 0 for numbers, false
for bool, "" for String, None for Option<T>, an empty Vec.
#[default] on an enum
An enum has no obvious default, so you nominate one:
#[derive(Default, Debug)]
enum Mode {
#[default]
Idle,
Fast,
Safe,
}
One restriction: #[default] works on unit variants only. Put it on
Fast(u32) and you get E0665, because the derive would have no way to
invent the payload.
The lint that teaches the idiom
field_reassign_with_default is on by default and fires on this:
let mut cfg = Cfg::default();
cfg.retries = 5; // error under -D warnings
The starter ships it. The fix is the struct-update form, and the lint is
right for reasons beyond style: the poke-after-build version needs mut
where the other does not, it constructs a value that is briefly wrong, and
with a #[non_exhaustive] struct from another crate it would not even
compile.
When Default is the wrong answer
This is the part usually left out. u16::default() is 0 and
String::default() is "" — both plausible-looking values that can be
silently, badly wrong. A Timeout of zero, a RetryLimit of zero, an empty
ApiKey: each of those is a bug that Default will hand you with a
straight face.
The alternative is to make the missing case a type error: no Default
impl, a constructor that demands the field, or an Option<T> that forces
the caller to decide. Default is right when “nothing specified” genuinely
has a sensible meaning, and wrong when it does not. Deriving it reflexively
on everything is a habit worth resisting.
Neighbouring lints
derivable_impls (which you met earlier) catches a hand-written Default
that duplicates the derive. default_trait_access (allow-by-default)
prefers Cfg::default() to Default::default() where the type is not
already obvious. new_without_default asks for a Default impl alongside a
public zero-argument new.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.