Skip to content

Conversation

@tisonkun
Copy link
Contributor

@tisonkun tisonkun commented Dec 15, 2025

This refers to #144426.

cc @andylokandy @jplatte

Updated with:

/// Create a new instance of `DropGuard` with a cleanup closure.
///
/// # Example
///
/// ```rust
/// # #![allow(unused)]
/// #![feature(drop_guard)]
///
/// use std::mem::defer;
///
/// let _guard = defer! { println!("Goodbye, world!") };
/// ```
#[unstable(feature = "drop_guard", issue = "144426")]
pub macro defer($($t:tt)*) {
    $crate::mem::DropGuard::new((), |()| { $($t)* })
}

Unfortunately, it's hard to convert impl FnOnce() to the input type param F: FnOnce(T) where T = ().

I tried:

impl<F> DropGuard<(), F>
where
    F: FnOnce(()),
{
    pub const fn new<G: FnOnce()>(f: G) -> Self {
        let f = move |()| f();
        Self { inner: ManuallyDrop::new(()), f: ManuallyDrop::new(f) }
    }
}

But it failed because the new closure has a different type of F.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Dec 15, 2025
@rustbot
Copy link
Collaborator

rustbot commented Dec 15, 2025

r? @scottmcm

rustbot has assigned @scottmcm.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

/// {
/// // Create a new guard that will do something
/// // when dropped.
/// let _guard = DropGuard::new(|()| println!("Goodbye, world!"));
Copy link
Contributor Author

@tisonkun tisonkun Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// let _guard = DropGuard::new(|()| println!("Goodbye, world!"));
/// let _guard = DropGuard::new(|| println!("Goodbye, world!"));

It's more proper to write || without (). But it seems quite hard to fine-tune the type parameter F: FnOnce(T) even if we are sure that T = (). impl FnOnce(()) is different from impl FnOnce().

@nxsaken
Copy link
Contributor

nxsaken commented Dec 15, 2025

I think the proposed API would be nice to have if it got rid of the unit argument, but as it is it feels like a half-measure.

I don't know if it would be popular, but maybe we could provide a defer! { ... } macro like the scopeguard crate does:

macro_rules! defer {
    ($($t:tt)*) => {
        let _guard = DropGuard::new((), |()| { $($t)* });
    };
}

This works too:

const fn defer<F: FnOnce()>(f: F) -> DropGuard<(), impl FnOnce(())> {
    DropGuard {
        inner: ManuallyDrop::new(()),
        f: ManuallyDrop::new(move |()| f()),
    }
}

@tisonkun
Copy link
Contributor Author

tisonkun commented Dec 16, 2025

const fn defer

This looks like a good solution. Let me try it out.

So far we export DropGuard as core::mem::DropGuard. A mem level core::mem::defer/core::mem::guard may be too generic, though.

@tisonkun
Copy link
Contributor Author

Updated with:

// core::mem::defer
pub const fn defer(f: impl FnOnce()) -> DropGuard<(), impl FnOnce(())> { }
// core::mem::guard
pub const fn guard<T, F>(value: T, f: F) -> DropGuard<T, F> { }

Maybe we should keep DropGuard::new and remove guard. But anyway the functionality is here and now we can consider the naming and API :|

@nxsaken
Copy link
Contributor

nxsaken commented Dec 16, 2025

I think we should keep DropGuard::new, and that defer should be a macro if it exists. Returning DropGuard<(), impl FnOnce(())> makes the guard's type unnameable and impossible to store in a struct, which seems too limiting for the standard library.

The macro is not perfect, e.g. I don't think you can have both defer! { foo() } and defer!(foo). Also, scopeguard's version of defer binds the guard to an unused variable. This guarantees that defer does what it says, but doesn't allow users to dismiss the guard. We could just return the guard and rely on #[must_use].

@tisonkun
Copy link
Contributor Author

@nxsaken Great suggestion! Updated.

/// ```
#[unstable(feature = "drop_guard", issue = "144426")]
pub macro defer($($t:tt)*) {
$crate::mem::DropGuard::new((), |()| { $($t)* })
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if |()| { ... } can proper move value when needed.

Added defer_moved_value test case and it seems to work properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants