/// A cancellation guard cancels an ongoing operation when dropped.
#[must_use = "Cancels the operation if dropped."]
trait Cancelable: Drop {}
trait MyApi {
fn start_op() -> impl Cancelable
}
This obviously gives me the well-known lint warning: "bounds on Self: core::ops::Drop are most likely incorrect, consider instead using core::mem::needs_drop to detect whether a type can be trivially dropped."
It's not my intention to strictly enforce anything, though. I just want to give implementors a hint what is expected. This is about ergonomics, not about safety or correctness: Implementing Cancelable without also implementing Drop is almost certainly wrong.
I've seen, though, that it has even been suggested to forbid this pattern altogether. So, what's so bad about this pattern?
What if I’m implementing Cancelable for something that contains something Drop?
That’s the fundamental way in which Drop bounds are mostly nonsensical: they don't specify whether a type does something when dropped, but whether it directly implements Drop, which should be considered an implementation detail.
Think about this. We all know what a file is. In Rust, std::fs::File is a thin wrapper over platform file descriptor and close() its backing handle on its impl Drop. Right?
Wrong! std::fs::File is a newtype over std::sys::fs::File, which on unix is a newtype over FileDesc which is a newtype over OwnedFd. And the OwnedFd only have impl Drop which close the fd. Do you think implementing Cancelable on File is wrong?