Add core::hint::must_use by dtolnay · Pull Request #94723 · rust-lang/rust (original) (raw)
The example code in this documentation is minimized from a real-world situation in the anyhow
crate where this function would have been valuable.
Having this provided by the standard library is especially useful for proc macros, even more than for macro_rules. That's because proc macro crates aren't allowed to export anything other than macros, so they couldn't make their own must_use
function for their macro-generated code to call.
Rendered documentation
An identity function that causes an
unused_must_use
warning to be triggered if the given value is not used (returned, stored in a variable, etc) by the caller.This is primarily intended for use in macro-generated code, in which a #[must_use] attribute either on a type or a function would not be convenient.
Example
#![feature(hint_must_use)]
use core::fmt;
pub struct Error(/* ... */);
#[macro_export] macro_rules! make_error { ($($args:expr),) => { core::hint::must_use({ let error = crate::makeerror(core::formatargs!(crate::make_error(core::format_args!(crate::makeerror(core::formatargs!(($args),)); error }) }; }
// Implementation detail of make_error! macro. #[doc(hidden)] pub fn make_error(args: fmt::Arguments<'_>) -> Error { Error(/* ... */) }
fn demo() -> Option { if true { // Oops, meant to write
return Some(make_error!("..."));
Some(make_error!("...")); } None }In the above example, we'd like an
unused_must_use
lint to apply to the value created bymake_error!
. However, neither#[must_use]
on a struct nor#[must_use]
on a function is appropriate here, so the macro expands usingcore::hint::must_use
instead.
- We wouldn't want
#[must_use]
on thestruct Error
because that would make the following unproblematic code trigger a warning:
fn f(arg: &str) -> Result<(), Error>#[test]
fn t() {
// Assert thatf
returns error if passed an empty string.
// A value of typeError
is unused here but that's not a problem.
f("").unwrap_err();
}
- Using
#[must_use]
onfn make_error
can't help because the return value is used, as the right-hand side of alet
statement. Thelet
statement looks useless but is in fact necessary for ensuring that temporaries within theformat_args
expansion are not kept alive past the creation of theError
, as keeping them alive past that point can cause autotrait issues in async code:
async fn f() {
// Usinglet
inside the make_error expansion causes temporaries like
//unsync()
to drop at the semicolon of thatlet
statement, which
// is prior to the await point. They would otherwise stay around until
// the semicolon on this statement, which is after the await point,
// and the enclosing Future would not implement Send.
log(make_error!("look: {:p}", unsync())).await;
}
async fn log(error: Error) {/* ... */}
// Returns something without a Sync impl.
fn unsync() -> *const () {
0 as *const ()
}