Skip to content

Tracking Issue for core::mem::DropGuard #144426

Closed

Description

@yoshuawuyts

View all comments

Feature gate: #![feature(drop_guard)]

This is a tracking issue for the core::mem::DropGuard API: a helper type that makes it easy to create impl Drop destructors inline.

Public API

/// Wrap a value and run a closure when dropped.
pub struct DropGuard<T, F: FnOnce(T)> { ... }

impl<T, F> DropGuard<T, F> {
    /// Create a new instance of `DropGuard`.
    pub fn new(inner: T, f: F) -> Self { ... }
    
    /// Consumes the `DropGuard`, returning the wrapped value.
    pub fn dismiss(guard: Self) -> T { ... }
}

impl<T, F> Deref for DropGuard<T, F> {
    type Target = T;
    fn deref(&self) -> &T { ... }
}
impl<T, F> DerefMut for DropGuard<T, F> { ... }
impl<T, F> Debug for DropGuard<T, F> { ... }
impl<T, F> Drop for DropGuard<T, F> { ... }

Example

#![feature(drop_guard)]

use std::mem::DropGuard;

{
    // Create a new guard around a string that will
    // print its value when dropped.
    let s = String::from("Chashu likes tuna");
    let mut s = DropGuard::new(s, |s| println!("{s}"));

    // Modify the string contained in the guard.
    s.push_str("!!!");

    // The guard will be dropped here, printing:
    // "Chashu likes tuna!!!"
}

Steps / History

(Remember to update the S-tracking-* label when checking boxes.)

Unresolved Questions

  • What should the name of the type be? DropGuard, WithDrop and Dropper were some of the preferred options raised in the 2025-07-22 T-Libs-API call.
  • What should the name of the method be for "take ownership of the value without running the destructor"? The name current name for this method is into_inner, but other names have been proposed, including: disarm, defuse, disable, and skip_drop.
  • Should the impl have a Clone impl? This can always be added in the future, but should be discussed further prior to stabilization.
  • Should the Debug impl be transparent? This can always be added in the future, but should be discussed further prior to stabilization.

Footnotes

  1. https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html ↩

Activity

  1. added
    T-libs-api[DEPRECATED; DO NOT USE]
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    on Jul 25, 2025
  2. changed the title [-]Tracking Issue for `std::mem::DropGuard`[/-] [+]Tracking Issue for `core::mem::DropGuard`[/+] on Jul 25, 2025
  3. RalfJung commented on Jul 28, 2025

    @RalfJung
    Member

    Another open question: what should into_inner be called? disarm might be a more evocative name.

  4. yoshuawuyts commented on Jul 28, 2025

    @yoshuawuyts
    MemberAuthor

    I've added it as an open question, and listed some of the other options people have brought up as well.

    I should add however that I personally dislike any names that evoke imagery of weapons and violence. If we can at all avoid that, I think we should.

  5. RalfJung commented on Jul 28, 2025

    @RalfJung
    Member

    That is fair. I just think that into_inner has the significant downside of being a very common method name, so one could easily think one sees a call to something else -- or one could refactor code to change the type, and the into_inner may keep working while changing its meaning. I am fine with basically an name that's not a common method name found on many types. :)

  6. yoshuawuyts commented on Jul 28, 2025

    @yoshuawuyts
    MemberAuthor

    Yeah I agree: into_inner is a fairly inconspicuous name for an operation that is often load-bearing. Having something that more clearly calls attention to itself seems like it might be worth it.

  7. FeldrinH commented on Jul 31, 2025

    @FeldrinH

    I just think that into_inner has the significant downside of being a very common method name, so one could easily think one sees a call to something else -- or one could refactor code to change the type, and the into_inner may keep working while changing its meaning.

    I don't think this is a problem in practice. DropGaurd::into_inner is an associated function, not a method, so you have to call it as DropGuard::into_inner(value), which makes it quite hard to confuse it for some other into_inner function and also makes it a compile error to pass a value of a different type.

  8. FeldrinH commented on Jul 31, 2025

    @FeldrinH

    FWIW I like the name into_inner. It is consistent with the existing scopeguard crate and also consistent with analogous methods on other types that affect drop behavior like ManuallyDrop and MaybeUninit.

  9. riking commented on Aug 15, 2025

    @riking

    I think the documentation on into_inner should also have an example of calling mem::forget(guard.into_inner()) since mem::forget is mentioned in the prose.

  10. karolzwolak commented on Aug 24, 2025

    @karolzwolak
    Member

    Implementation PR is merged, so @rustbot label -S-tracking-unimplemented

  11. tisonkun commented on Nov 25, 2025

    @tisonkun
    Contributor

    Upon dismiss naming, I wonder if we can avoid requiring pass in a value since a move || closure would already handle the ownership issue.

    Then dismiss can even return ().

    pub struct DropGuard<F = fn()>
    where
        F: FnOnce(),
    {
        f: ManuallyDrop<F>,
    }
    
    impl<F> DropGuard<F>
    where
        F: FnOnce(),
    {
        pub const fn new(f: F) -> Self {
            Self { f: ManuallyDrop::new(f) }
        }
    
        pub fn dismiss(guard: Self) -> () {
            let mut guard = ManuallyDrop::new(guard);
            unsafe { ManuallyDrop::drop(&mut guard.f) };
        }
    }
    
    impl<F> Drop for DropGuard<F>
    where
        F: FnOnce(),
    {
        fn drop(&mut self) {
            let f = unsafe { ManuallyDrop::take(&mut self.f) };
            f();
        }
    }

    In most cases, the pass-in value is (), and we'd seldom use Deref/DerefMut.

  12. tisonkun commented on Nov 25, 2025

    @tisonkun
    Contributor

    .. or have an ergonomic simple entry point when the value is ():

    impl <F> DropGuard<(), F>
    where
        F: FnOnce(()),
    {
       pub const fn from_fn<FN: FnOnce()>(f: FN) -> Self {
            let f = move |()| f();
            Self::new((), f)
        }
    }
  13. 32 remaining items

  14. danielhenrymantilla commented on Aug 22, 2026

    @danielhenrymantilla
    Contributor

    Regarding naming1, .dismantle() might be what I've found most befitting. Indeed it is a word which conveys both:

    • the deäctivation/disabling (or using pre-existing terminology in third-party crates, defusing/disarming) of the drop glue;
    • the .into_inner() deconstruction that it results into.

    And it is not itself weaponry-related (you can dismantle an operation, for instance).


    @ais523 Regarding the drop glue, I think that an API along the lines of https://docs.rs/safe-manually-drop would fit it better, insofar the drop logic would be written as an impl rather than as a closure inside the constructor. Though perhaps we could unify both ideas by using a proxy trait for the second type parameter of the DropGuard, like this:

    pub struct DropGuard<T, F>(
        MD<T>,
        MD<F>,
    )
    where
        F: DropManually<T>,
    ;
    
    pub trait DropManually<T> {
        fn drop_manually(self, field: T);
    }
    
    impl<F: FnOnce(T), T> DropManually<T> for F {
        fn drop_manually(self, field: T) {
            self(field)
        }
    }
    
    impl<T, F: DropManually<T>> Drop for DropGuard<T, F> {
        fn drop(&mut self) {
            F::drop_manually(
                unsafe { MD::take(&mut self.1) },
                unsafe { MD::take(&mut self.0) },
            )
        }
    }

    This would retain compatibility with the current API, but would also allow your use-case, @ais523:

    Click to see
    pub struct CellGuard<'a, T>(
        DropGuard<CellGuardFields<'a, T>, PD<Self>>,
    );
    // where:
    struct CellGuardFields<'a, T> {
        cell: &'a Cell<Option<T>>,
        val: T
    }
    
    impl<'a, T> DropManually<CellGuardFields<'a, T>> for PD<CellGuard<'a, T>> {
        fn drop_manually(self, CellGuardFields { cell, val }: CellGuardFields<'a, T>) {
            cell.set(Some(val))
        }
    }

    which makes the drop glue stand out more (à la impl Drop), than in:

    pub struct CellGuard<'a, T>(
        DropGuard<CellGuardFields<'a, T>, CellGuardDestructor<'a, T>>,
    );
    // where
    struct CellGuardFields<'a, T> {
        cell: &'a Cell<Option<T>>,
        val: T
    }
    
    type CellGuardDestructor<'a, T> = impl FnOnce(CellGuardFields<'a, T>);
    
    impl<'a, T> CellGuard<'a, T> {
        #[define_opaque(CellGuardDestructor)]
        pub fn new(o: &'a Cell<Option<T>>) -> Self {
            CellGuard(DropGuard::new(
                CellGuardFields {
                    cell: o,
                    val: o.take().unwrap()
                },
                |cgf| cgf.cell.set(Some(cgf.val))))
        }

    Though after writing all this I am starting to think there may be more clarity in having both SafeManuallyDrop and DropGuard than trying to shoehorn the two together, since I am not 100% fond of the PD<…> layer with DropGuard (and also, the "non-entailed FnOnce" would probably hamper closure arg inference in the more typical DropGuard intended case of the OP).

    Footnotes

    1. (as I have not seen any explicit mentions/considerations in the latest minutes of the candidate names suggested on Zulip) ↩

  15. added a commit that references this issue on Aug 25, 2026
  16. added 2 commits that reference this issue on Aug 27, 2026
  17. added 5 commits that reference this issue on Aug 28, 2026
  18. added a commit that references this issue on Aug 31, 2026
  19. added a commit that references this issue on Sep 13, 2026
  20. added a commit that references this issue on Sep 13, 2026
  21. added 2 commits that reference this issue on Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCS-tracking-ready-to-stabilizeStatus: This is ready to stabilize; it may need a stabilization report and a PRT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions