We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Errors Are Values step 21 of 24
#[must_use] in depth
Fix the annotations on a small builder API, then fix the code that misuses it.
pub fn build(ops: Vec<String>) -> Vec<String>
build starts from Config::default() (localhost:80) and runs a list of
ops:
| op | effect | pushes |
|---|---|---|
"port:N" |
with_port(N) |
— |
"host:H" |
with_host(H) |
— |
"reset" |
back to the defaults | — |
"show" |
— |
"{host}:{port}" |
"validate" |
— |
"ok {host}:{port}" or "err {message}" |
| anything else | — |
"unknown" |
Validation rejects an empty host ("host must not be empty", checked first)
and any port below 1024 ("port 80 is privileged").
Three annotations, and two of them are wrong
The starter marks everything #[must_use], which is the mistake people make
after being told the attribute is good. Clippy names both errors:
error: this function has a `#[must_use]` attribute with no message, but
returns a type already marked as `#[must_use]`
|
| #[must_use]
| pub fn validate(self) -> Result<Validated, String> {
clippy::double_must_use is on by default. Result is already #[must_use],
so annotating the function adds nothing — it just makes the diagnostic worse,
because the generic “unused return value” replaces Result‘s specific “this
Result may be an Err variant”. If you want a better message, give the
attribute one: #[must_use = "…"]. If you have nothing to add, leave it off.
error: this unit-returning function has a `#[must_use]` attribute
|
| #[must_use]
| pub fn reset(&mut self) {
clippy::must_use_unit, also on by default. A function returning () has no
value to use, so the attribute makes every call site a warning. Look at the
starter’s compile output: unused return value of Config::reset that must be used fires on a perfectly ordinary cfg.reset();.
The one that stays is with_port, and it is worth being clear about why:
#[must_use]
pub fn with_port(self, port: u16) -> Self { … }
It takes self by value and returns a new Config. Ignoring the result
does not modify anything — it silently discards the change. That is a bug the
compiler can catch for you, and the starter’s build contains exactly that
bug.
Contrast a &mut self setter — fn set_port(&mut self, port: u16) — which
mutates in place and returns nothing. That one needs no annotation, and would
be must_use_unit if you added one. The attribute belongs on the methods
where “call it and ignore it” is meaningless.
Why the Validated newtype is annotated instead
#[must_use]
pub struct Validated(Config);
Three kinds of #[must_use] check exist, and this is the first:
-
type-based — on a struct, enum, union or trait. Every expression of that
type must be used.
Result,IteratorandMustUseadaptors work this way. - function-based — on a function or method. Its return value must be used.
-
trait-based — on a trait, covering
impl Traitreturn positions.
Marking the type is the stronger and more maintainable choice: it covers every function that ever produces one, including ones written years later. Marking the function is right when the type is generally fine to discard but this particular function’s result is not.
The Reference adds a rule that surprises people: #[must_use] has no effect
on trait impl items. Annotating fn fmt inside an impl Display does
nothing. Put it on the trait’s declaration instead.
What counts as “using” it
Verified on this toolchain:
f(); // warns
{ f() }; // warns — the lint looks THROUGH a block
Some(f()); // silent — wrapping counts as a use
(f(),); // silent — same
let _ = f(); // silent — the sanctioned opt-out
_ = f(); // silent — same, since 1.59
The two silent middle cases are worth noticing, because they are how a value
escapes the check without anyone meaning it to. They are not loopholes to
reach for — let _ = is the one that says “deliberate” to a reviewer.
Notes
-
Under
-D warnings, all of this has teeth:unused_must_useis in rustc’sunusedgroup, so an ignoredResultis a build failure, not a nag. -
must_use_candidate(pedantic, off here) suggests adding the attribute to every pure public function that returns something. It is a lot of noise; most crates leave it off and annotate deliberately. -
return_self_not_must_use(pedantic) is the specific case this problem is built on: a method returningSelfalmost always wants#[must_use]. -
validateconsumesself, sobuildclones before calling it — otherwise the config could not be shown afterwards.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.