Return error condition from Drop impl


#1

Hi,

I have a object which has a Drop impl which performs a, maybe failing, action. Is there a possibility to return a error condition somehow?

panic! is a bit too aggressive for this purpose, as it’s not uncommon that the action fails.

The object is sharing a ctx struct, I thought of setting an error flag there which can be checked, just like C’s errno. Is there some more elegant way or pattern of doing this? Perhaps by creating a proxy object on creation with which the error can be delivered?

The code is here

Thanks, Falco


#2

In general it is not possible to return an error condition from drop. You can panic! or do a best effort cleanup and log an error message (or post it to some global channel).

However, if failure is not exceptionally rare, I would go for an explicit deallocation API along this lines

struct S {
    destroyed: bool // false for new instances
}

impl S {
    fn destroy(mut self) -> Result<()> { // takes self by value and hence can't be called twice
        self.destoryed = true;
        // do cleanup
    }
}

impl Drop for S {
    fn drop(&mut self) {
        if !self.destroyed {
            panic!("not destroyed S instance found!"); // or may be `error!(...)`
        }
    }
}

#3

The idea if my drop impl, was to do some automatic action on drop, in this case, toggle some bits on an external IO device.

In your approach the automatic feature gets lost if I understand correctly.


#4

What if you never allowed anyone to own the object?

pub struct Object { ... }

impl Object {
    pub fn do_stuff(&mut self) { ... }
    fn finalize(self) -> Result { ... }

    pub fn with<F: FnOnce(&mut Self) -> Result>(func: F) -> Result {
        let mut object = Object { ... };
        try!(func(&mut object));
        object.finalize()
    }
}

#5

I use a TryDrop trait for things like this:

pub trait TryDrop: Drop + Sized {
    type Err: Error;

    fn try_drop(mut self) -> Result<(), Self::Err> {
        unsafe {
            let r = self.try_drop_inner();
            mem::forget(self);
            r
        }
    }

    unsafe fn try_drop_inner(&mut self) -> Result<(), Self::Err>;
}

For example:

impl Drop for Wnd {
    fn drop(&mut self) {
        unsafe { self.try_drop_inner().unwrap() }
    }
}

impl TryDrop for Wnd {
    type Err = io::Error;

    unsafe fn try_drop_inner(&mut self) -> Result<(), Self::Err> {
        match user32::DestroyWindow(self.0) {
            0 => last_error(),
            _ => Ok(())
        }
    }
}

#6

The value of the Drop trait is in its implicitness, so it’s inherently somewhat at odds with error handling, which Rust philosophy is to make explicit. For protecting a dynamic scope, @gkoz’s answer would be a decent idea.

Explicit drop functions would work well if we had a way to prevent you from leaving off the drop call (and getting a compiler-generated call to the implicit drop). Is https://github.com/rust-lang/rfcs/issues/814 still the most current proposal for that? Anyway, we can’t prevent dropping entirely because the thread might be in the middle of a panic.