I have a language feature idea that developed out of a need to make a type always live for as long as possible. I don’t know if the idea is smart and useful, or of the code pattern I used that made this seem desirable is Bad, Actually, and I should structure my own library in such a way as to never need this feature. Anyway.
For a project of mine, I started using the pattern where some open_resource()
function returns a handle, whose only job is to call close_resource()
when it falls out of scope. Kind of like file objects, except the handle has no way to actually interact with it, so the user might never think to assign the handle to a variable. And that causes the handle to Drop
immediately.
As an example, imagine a register_some_callback
function that takes a closure, somehow registers it in an event system across an FFI boundary, then returns a CallbackHandle
struct whose Drop
trait unregisters that same closure from the foreign library’s event system.
let handle = register_some_callback(|| {
println!("this works great!");
println!("at the end of this scope, a Drop impl unregisters this callback");
});
register_some_callback(|| {
println!("this handle gets dropped immediately! :(");
println!("Definitely not what the programmer intended");
});
And I thought it would be handy if we were able to annotate return values, or the types themselves, with an attribute that prevents being automatically dropped like this?
Now we do have the option of annotating a type with #[must_use]
but that only produces a warning, and there’s still a corner case it doesn’t hit.
let handle = register_some_callback(|| {
println!("works as intended!");
println!("Though we'll likely get a warning: 'handle not used, consider using _handle'");
println!("Because handle has no methods to actually use.");
});
register_come_callback(|| {
println!("Return value not used! Warning works as intended.");
println!("Though the code is still broken if the programmer ignores it...");
});
let _handle = register_some_callback(|| println!("No warnings, just right. :)"));
let _ = register_some_callback(|| {
println!("Return value is immediately dropped! AND no warning is produced.");
println!("Maybe the programmer thought this has the same semantics as assigning to _handle?");
});
Thus, I came to the idea that an annotation like #[no_auto_drop]
would be handy for types whose sole purpose is to live its longest possible scope, then presumably Drop
with some side effect whose ordering matters.
I looked at the ManuallyDrop
trait, and that seems more geared toward giving authors precise control over drop ordering, whereas this is about relieving authors from needing to care whether or not a type’s Drop
trait affects their program.
Is this worth drafting up an RFC for? If so, I’d like to, as well as work on the feature. I’ve done a cursory look through rustc code, and I think I’m capable of implementing it with some mentoring.