I just realized this only works with auto traits (like you said). I played around a bit:
#![feature(trait_upcasting)]
fn execute(run: Box<dyn FnOnce()>) {
run()
}
trait Dropable {}
impl<T: ?Sized> Dropable for T {}
fn nop(value: Box<dyn Dropable>) {
// we can't really do anything here but dropping the value
println!("Before drop");
drop(value);
println!("After drop");
}
struct DropMonitor {}
impl Drop for DropMonitor {
fn drop(&mut self) {
println!("DropMonitor was dropped")
}
}
fn main() {
let closure1: Box<dyn FnOnce() + Send + Sync> = Box::new(|| {
println!("Hello One");
});
execute(closure1);
let closure2: Box<dyn Fn() + Send + Sync> = Box::new(move || {
println!("Hello Two");
});
execute(closure2); // this requires #![feature(trait_upcasting)]
let monitor = DropMonitor {};
let closure3: Box<dyn FnOnce() + Send + Sync> = Box::new(move || {
let _monitor = monitor;
println!("Hello Three");
});
// This won't work at all:
// nop(closure3);
// Neither does this:
// nop(closure3 as Box<dyn Dropable>);
// But we can do:
nop(Box::new(closure3));
}
Output:
Hello One
Hello Two
Before drop
DropMonitor was dropped
After drop
Note that we cannot cast or coerce Box<dyn FnOnce() + Send + Sync>
into Box<dyn Dropable>
. I assume that's because of the vtable being different, right?
Interestingly, it's possible to wrap it in another box to make it Dropable
and thus passable to nop
. (Side note: That trick can help me to be generic over the argument and return type of a closure in my ephemeral closure experiment.)