Attributes and Conditional Compilation

#[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) -> &str is annotated with #[must_use = "this returns the trimmed string as a slice, without modifying the original"], ignoring s.trim(); immediately warns the developer.
  • Because trait Iterator and trait Future are annotated with #[must_use], forgetting .collect() on an iterator or forgetting .await on an async fn call 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.