From 53f2837f701f4d0f08565957e7d65b4c2df0f71a Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 13 Aug 2026 17:00:59 -0700 Subject: [PATCH] implement try_unwrap to allow a custom Drop to drop iteratively --- src/lib.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e07ef09..88a8389 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -335,6 +335,40 @@ impl HConsed { pub fn arc_count(&self) -> usize { Arc::strong_count(&self.elm) } + + /// Returns the element if `this` is the last strong reference to it, and `this` itself + /// otherwise. + /// + /// Succeeds even when weak references are outstanding, such as the entry a consign keeps for + /// the element: those weak references stop upgrading afterwards, exactly as they do when the + /// last `HConsed` is dropped. + /// + /// This is the only way to take ownership of an element instead of letting the `Arc` drop it + /// in place, which is what makes stack-free deallocation of deeply nested elements possible. + /// Dropping a `HConsed` whose element holds other `HConsed`s recurses once per nesting level, + /// so a deep enough element overflows the native stack — see `examples/deep_drop.rs`. With + /// `try_unwrap`, a `Drop` implementation on the element type can move its children onto an + /// explicit worklist before the element is dropped, keeping the stack depth constant. + /// + /// ```rust + /// use hashconsing::{HConsed, HConsign, HashConsign}; + /// + /// let mut factory: HConsign = HConsign::empty(); + /// let elm = factory.mk(1); + /// + /// // The consign only keeps a weak reference, so `elm` is the last strong one. + /// let clone = elm.clone(); + /// let elm = HConsed::try_unwrap(elm).expect_err("`clone` is still around"); + /// drop(clone); + /// assert_eq!(HConsed::try_unwrap(elm), Ok(1)); + /// + /// // The consign's entry can no longer be upgraded, so it hands out a fresh element. + /// assert_eq!(*factory.mk(1).get(), 1); + /// ``` + pub fn try_unwrap(this: Self) -> Result { + let HConsed { elm, uid } = this; + Arc::try_unwrap(elm).map_err(|elm| HConsed { elm, uid }) + } } impl fmt::Debug for HConsed {