Conditionally implementing Drop depending on T

I've got an interesting and confusing error that can be seen in this playground example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f397a36f406ec6e1ddf6a8125286ec27. It seems to indicate that I can't implement Drop for my type if that implementation requires a generic T: Drop. I find this surprising since I apply conditions to generic parameters for trait implementations all the time.

I have three questions: Am I interpreting this error correctly? If so, why is this a restriction? Lastly, what is the recommended work-around, considering that I only need to implement Drop for my type if T: Drop?

To give some extra context: I'm writing some bare-metal code, and I'm trying to create a wrapper type that will disable CPU interrupts and preemption while accessing the inner object. This is, in essence, a convenient way of creating critical sections of code. For what I'm doing, I believe it makes sense that "accessing the inner object" should include that object's destructor.

It is possible that I may be able to avoid disabling preemption during the destructor, I'm not actually sure at this point, but I'd rather be safe than sorry. I could also always implement Drop regardless of whether or not T does, but that just feels like a silly compromise since it relies on compiler optimizations to achieve the same generated code size and performance.

1 Like

I am not entirely sure of the reasoning why it should not be allowed to implement Drop only sometimes, depending on T, but

this is wrong!

T: Drop means that the type T itself defines a destructor, however not having T: Drop does not mean that for x: T, the operation drop(x) (or similarly drop_in_place) is a no-op.

For example Option<T> does not implement Drop. When you drop a Option<String> there is however most certainly something happening.

what you need to consider to know if it’s sure to not drop a value is std::mem::needs_drop. As the docs say:

pub const fn needs_drop<T>() -> bool
[...]
if this function actually returns false , then you can be certain dropping T has no side effect

In your case I would recommend to just implement Drop while you can avoid extra cost by stopping and re-enabling the CPU interrups by wrapping the drop implementation in if needs_drop::<T>() { ... }. Since that condition is using a const fn, I would rather confidently rely on it being optimized away.

2 Likes

Conditions in Drop must be the same as in the struct definition. If you use:

struct Critical<T: Drop> {
    protected: ManuallyDrop<T>,
}

then it will compile. i.e. you can't have specialization for Drop. It must be the same for all type arguments.

However, Drop bound is useless and misleading. You should never use it. It was almost removed from the language (but now it can't due to backwards compat). Types that don't implement Drop still may have destructors when they have fields with Drop.

As a bound only T: Copy works, but implies the opposite: that the type will never have Drop, and won't contain anything with Drop in it.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.