An object that is an error to drop

Suppose we have:

pub struct ResourceManager {}

pub struct Ptr_To_Some_Resource {}

Now, whenever a Ptr_To_Source_Resource is dropped, we want ResrouceManager to be notified, so we do something like this:

pub struct Ptr_To_Some_Resource{
  resource_manager: Rc<ResourceManager>
}

impl Drop for Ptr_To_Some_Resource {
  // use self.resource_manager to notify
}

Now, alternatively I am wondering if we can do something else: make it a compile time error for Ptr_To_Some_Resource to drop ?

Basically I want something where Ptr_To_Some_Resource can only be destroyed manually via Resource_Manager, and an error if it drops via any other way.

=== EDIT

pub struct ResourceManager {}

impl ResourceManager {

  pub fn create() -> Ptr_To_Some_Resource ;

  // only valid way to destroy a Ptr_To_Some_Resource
  // make just dropping a Ptr_To_Some_Resource a compile time error
  pub fn destroy(&mut self, p: Ptr_to_Some_Resource); 
}

Rust has something called an "affine" type system where values can be used at most once (it's the technincal term for move semantics), whereas in a "linear" type system a value must be used exactly once (imagine needing to call something taking self by value in every possible code path).

That means there isn't going to be a way to - at compile time - enforce destroy() is always called. The next best thing is probably to enforce it at runtime using the drop_bomb crate.

Although, if it were me then I'd probably prefer the impl Drop for Ptr_To_Some_Resource method. As a side note, if you use some sort of shared communication mechanism like channels instead of holding an Rc<ResourceManager> directly, the ResourceManager won't be forced to use &self methods and interior mutability.

In this case, Ptr_To_Some_Resource essentially couldn't be passed anywhere by value, except mem::forget, since by transferring ownership we make the called function responsible for dropping the now-owned value. Is this what you've intended?

pub fn destroy(&mut self, p: Ptr_to_Some_Resource); {
  // do some processing work
  let Ptr_To_Some_Resource { ... } = p;
}

Would the above work? The theory being we deconstruct p and drop its members separately, but never drop p itself.

But if Ptr_to_Some_Resource can't be dropped, then this function is illegal to call - its signature doesn't guarantee that the value isn't dropped inside it, so the caller would conservatively assume it it.

That's not how destructuring works. You'd just get &mut references to each of the members, which doesn't let you drop them unless you use something like Option::take()[1].


  1. Let's not even go into ManuallyDrop, which would be a footgun waiting to happen. ↩︎

You could put an invalid link symbol in the Drop implementation - this doesn't produce a pretty compiler error but it does produce one (you have to mem::forget the value in the "manual" destructor).

It will likely also false-positive in debug builds.

Are we looking at the same piece of code? the Ptr_To_Some_Resource here is not a &mut.