Overlapping implementations in std::error

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));
}

(Playground)

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.)

1 Like