Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add core::hint::must_use #94723

Merged
merged 1 commit into from
Mar 9, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions library/core/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,126 @@ pub fn spin_loop() {
pub const fn black_box<T>(dummy: T) -> T {
crate::intrinsics::black_box(dummy)
}

/// 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][must_use] either on a type or a function would not
/// be convenient.
///
/// [must_use]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
///
/// # 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::make_error(core::format_args!($($args),*));
/// error
/// })
/// };
/// }
///
/// // Implementation detail of make_error! macro.
/// #[doc(hidden)]
/// pub fn make_error(args: fmt::Arguments<'_>) -> Error {
/// Error(/* ... */)
/// }
///
/// fn demo() -> Option<Error> {
/// if true {
/// // Oops, meant to write `return Some(make_error!("..."));`
/// Some(make_error!("..."));
/// }
/// None
/// }
/// #
/// # // Make rustdoc not wrap the whole snippet in fn main, so that $crate::make_error works
/// # fn main() {}
/// ```
///
/// In the above example, we'd like an `unused_must_use` lint to apply to the
/// value created by `make_error!`. However, neither `#[must_use]` on a struct
/// nor `#[must_use]` on a function is appropriate here, so the macro expands
/// using `core::hint::must_use` instead.
///
/// - We wouldn't want `#[must_use]` on the `struct Error` because that would
/// make the following unproblematic code trigger a warning:
///
/// ```
/// # struct Error;
/// #
/// fn f(arg: &str) -> Result<(), Error>
/// # { Ok(()) }
///
/// #[test]
/// fn t() {
/// // Assert that `f` returns error if passed an empty string.
/// // A value of type `Error` is unused here but that's not a problem.
/// f("").unwrap_err();
/// }
/// ```
///
/// - Using `#[must_use]` on `fn make_error` can't help because the return value
/// *is* used, as the right-hand side of a `let` statement. The `let`
/// statement looks useless but is in fact necessary for ensuring that
/// temporaries within the `format_args` expansion are not kept alive past the
/// creation of the `Error`, as keeping them alive past that point can cause
/// autotrait issues in async code:
///
/// ```
/// # #![feature(hint_must_use)]
/// #
/// # struct Error;
/// #
/// # macro_rules! make_error {
/// # ($($args:expr),*) => {
/// # core::hint::must_use({
/// # // If `let` isn't used, then `f()` produces a non-Send future.
/// # let error = make_error(core::format_args!($($args),*));
/// # error
/// # })
/// # };
/// # }
/// #
/// # fn make_error(args: core::fmt::Arguments<'_>) -> Error {
/// # Error
/// # }
/// #
/// async fn f() {
/// // Using `let` inside the make_error expansion causes temporaries like
/// // `unsync()` to drop at the semicolon of that `let` 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 ()
/// }
/// #
/// # fn test() {
/// # fn assert_send(_: impl Send) {}
/// # assert_send(f());
/// # }
/// ```
#[unstable(feature = "hint_must_use", issue = "94745")]
#[rustc_const_unstable(feature = "hint_must_use", issue = "94745")]
#[must_use] // <-- :)
pub const fn must_use<T>(value: T) -> T {
value
}