How to force drop order of struct fields


#1

Is there any way to efficiently force Drop order of struct fields? or a workaround?

I know that Drop order is currently undefined in Rust (https://github.com/rust-lang/rfcs/issues/744), but I need to make sure that fields get dropped in certain order, because I’m using a C library that will not get cleaned up properly otherwise.

example:

struct Foo {
    a:A,
    b:B,
    c:C,
}

I want to make sure fields get dropped in order C,B,A.
Not A,B,C, like how Rust seems to do (most of the time), which will break cleanup functions for B and C.

The only safe solution I have found so far is to wrap all fields in Option and then call x.take()'s in Drop to force the drop order, but that is pretty silly.


#2

I’m only guessing, but I’m assuming it is because C requires B and A, and B requires A (simplified)? If so, you might be able to wrap it in types for different states of construction/destruction:

struct StateA { a: A }
struct StateB { b: B, state_a: StateA }
struct StateC { c: C, state_b: StateB }

struct Foo { state_c: StateC }

There might be better names for your situation. And of course, it might turn out to be more sillier for your case than the Option<T> variant. Plus it’s monday and I might be missing something.


#3

yes

I’m bit doubtful this works.
Since the drop order is undefined, StateB for example could drop state_a before b.
This would mean that a gets dropped before b. Right?


#4

Yeah, I think you’re right. Sorry about the noise, like I said “monday” :slight_smile:


#5

Do you really need A, B and C to be Drop by themselves? If they depend on each other maybe it’s better to implement Drop only for containing struct?


#6

Let me elaborate.

I’m using rust-sdl2 (https://github.com/AngryLawyer/rust-sdl2).
I want to store different SDL handles on a single struct so that i don’t have to pass multiple context parameters to functions that need access to them.

Code looks like this

pub struct Context {
    pub sdl: sdl2::Sdl,
    pub video: sdl2::VideoSubsystem, // depends on sdl
    pub renderer: sdl2::render::Renderer<'static>, // depends on video
    pub event_pump: sdl2::EventPump, // depends on sdl
    .. // other handles for sdl_mixer etc.
}

impl Context {
    pub fn new() -> Result<Context, String> {
        ..
        let sdl = sdl2::init().unwrap();
        let video = sdl.video().unwrap();
        ..
        let mut renderer = window.renderer().build().unwrap();
        ..
        Ok(Context{ sdl:sdl, video:video, renderer:renderer })
    }
}

Now, I suppose I could store borrow references in the struct as well, but then the user would have to initialize SDL and pass the references himself.


#7

As the poster above you said: implement Drop manually on the struct and call the drop method on the fields in dependency order.


#8

If I try to call drop() manually I get error: error: explicit use of destructor method [E0040]
https://doc.rust-lang.org/error-index.html#E0040


#9

Don’t call it manually. It will be called after variable goes out of scope
or use
https://doc.rust-lang.org/std/mem/fn.drop.html
which has very interesting implementation, just look at its source


#10

Unfortunatelly std::mem::drop won’t help here, because you can’t drop fields of a mutable reference.

@fzzy you can store references in the Context and still avoid

the user would have to initialize SDL and pass the references himself.

If you use a closure based API

fn withContext<F>(
    callback: F // user creates the callback
)
    where F: FnOnce(&mut Context) 
{
    let sdl = ...;
    let video = ...;
    let ctx = Context { video: &mut video, sdl: &mut sdl, ...};
    callback(&mut ctx);
}

Alternatively, you can use a by-value Context and require the user to call a special cleanup function:

fn cleanump(ctxByValue: Context) {
    let Context {sdl, video, ...} = ctxByValue;
    drop(video);
    drop(sdl);
}

#11

[quote=“fzzy, post:1, topic:6203”]
I need to make sure that fields get dropped in certain order, because I’m using a C library that will not get cleaned up properly otherwise.
[/quote]Are you certain of this? This would be a bug in the bindings. After looking at rust-sdl2 sources it seems they go to lengths to make sure cleanup is performed in correct order.


#12

Oh, it’s like that.
Nevermind then I’m a dummie.