#[must_use]
#[must_use] (and #[must_use = "custom diagnostic message"]) is an outer attribute that can be applied to structs, enums, unions, traits, and fns. When an expression evaluates to a #[must_use] type or returns from a #[must_use] function and its value is ignored in an expression statement (call(); without binding it to let _ = call(); or a variable), the compiler emits the unused_must_use warning.
Where #[must_use] Can Be Applied
| Target | Effect | Standard Library Examples |
|---|---|---|
Type (struct, enum, union) |
Every function returning this type triggers unused_must_use if the return value is ignored |
Result<T, E>, ControlFlow<B, C>, MutexGuard<'_, T>, NonNull<T> |
Trait (trait) |
Every function returning impl MyTrait or Box<dyn MyTrait> triggers unused_must_use if ignored |
Future, Iterator, DoubleEndedIterator |
Function / Method (fn) |
Ignoring the return value of this specific function triggers unused_must_use (even if the return type is bool, usize, or Self) |
str::trim(), i32::checked_add(), Vec::split_off() |
Why Pure Functions and Lazy Combinators Need #[must_use]
A common bug for newcomers is calling s.trim(); or iter.map(|x| x + 1); as a standalone statement, thinking it mutates s in place or executes the iterator immediately:
- Because
str::trim(&self) -> &stris annotated with#[must_use = "this returns the trimmed string as a slice, without modifying the original"], ignorings.trim();immediately warns the developer. - Because
trait Iteratorandtrait Futureare annotated with#[must_use], forgetting.collect()on an iterator or forgetting.awaiton anasync fncall immediately triggers a clear compiler warning!
#[must_use = "ValidationTicket does nothing unless `.commit()` is called"]
pub struct ValidationTicket {
user_id: u64,
}
impl ValidationTicket {
pub fn verify(user_id: u64) -> Self {
Self { user_id }
}
pub fn commit(self) {
println!("committed validated action for user {}", self.user_id);
}
}
pub struct Counter(u64);
impl Counter {
#[must_use = "`.incremented()` returns a new Counter and does not modify `self`"]
pub const fn incremented(&self) -> Self {
Self(self.0 + 1)
}
}
fn main() {
let ticket = ValidationTicket::verify(42);
ticket.commit();
let c1 = Counter(10);
let c2 = c1.incremented();
println!("c1 = {}, c2 = {}", c1.0, c2.0);
}
committed validated action for user 42
c1 = 10, c2 = 11
Intentionally Discarding a #[must_use] Value (let _ = expr;)
When you intentionally want to ignore a #[must_use] return value (for example, sending a shutdown notification over a channel where the receiver may have already disconnected), binding the result to the wildcard pattern let _ = expr; explicitly acknowledges the return value and silences unused_must_use.