How can I move a value out of the argument to Drop::drop()?











up vote
6
down vote

favorite












I'm using gfx-hal, which requires me to create resources which need to be explicitly destroyed using functions specific to their type. I'd like to store instances of these types in structs, and I'd also like to tie cleaning them up to the lifetime of the owning struct, instead of managing their lifetimes manually and potentially having objects on the GPU/in the driver live forever.



However, all the functions in the destroy family of functions take the type directly, rather than a reference, so when I try to pass them from my structs, I get errors like the following:



error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
--> src/lib.rs:9:18
|
9 | destroyT(self.member)
| ^^^^^^^^^^^ cannot move out of here


It seems like there should be some way around this issue, as I'm currently in the Drop::drop function itself, so self is already "consumed." How do I get the instances of these types out of self as T, and not &T?



struct T;

struct S {
member: T,
}

impl Drop for S {
fn drop(&mut self) {
destroyT(self.member)
}
}

// elsewhere, in a library

fn destroyT(t: T) {
//...
}









share|improve this question




















  • 2




    Looks like you're not the only one to find this frustrating: github.com/gfx-rs/gfx/issues/2452
    – loganfsmyth
    Nov 12 at 2:07










  • Couldn't you use a NewType for T that implements Drop and calls destroy(). That way, the Drop for S would be automatically generated.
    – rodrigo
    Nov 12 at 10:29






  • 1




    @rodrigo Isn't that exactly what the OP was trying to do?
    – Sven Marnach
    Nov 12 at 10:52










  • @SvenMarnach: Oh, I see. I was assuming S was some composite type. But then the NewType would be exactly like this S.
    – rodrigo
    Nov 12 at 11:02















up vote
6
down vote

favorite












I'm using gfx-hal, which requires me to create resources which need to be explicitly destroyed using functions specific to their type. I'd like to store instances of these types in structs, and I'd also like to tie cleaning them up to the lifetime of the owning struct, instead of managing their lifetimes manually and potentially having objects on the GPU/in the driver live forever.



However, all the functions in the destroy family of functions take the type directly, rather than a reference, so when I try to pass them from my structs, I get errors like the following:



error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
--> src/lib.rs:9:18
|
9 | destroyT(self.member)
| ^^^^^^^^^^^ cannot move out of here


It seems like there should be some way around this issue, as I'm currently in the Drop::drop function itself, so self is already "consumed." How do I get the instances of these types out of self as T, and not &T?



struct T;

struct S {
member: T,
}

impl Drop for S {
fn drop(&mut self) {
destroyT(self.member)
}
}

// elsewhere, in a library

fn destroyT(t: T) {
//...
}









share|improve this question




















  • 2




    Looks like you're not the only one to find this frustrating: github.com/gfx-rs/gfx/issues/2452
    – loganfsmyth
    Nov 12 at 2:07










  • Couldn't you use a NewType for T that implements Drop and calls destroy(). That way, the Drop for S would be automatically generated.
    – rodrigo
    Nov 12 at 10:29






  • 1




    @rodrigo Isn't that exactly what the OP was trying to do?
    – Sven Marnach
    Nov 12 at 10:52










  • @SvenMarnach: Oh, I see. I was assuming S was some composite type. But then the NewType would be exactly like this S.
    – rodrigo
    Nov 12 at 11:02













up vote
6
down vote

favorite









up vote
6
down vote

favorite











I'm using gfx-hal, which requires me to create resources which need to be explicitly destroyed using functions specific to their type. I'd like to store instances of these types in structs, and I'd also like to tie cleaning them up to the lifetime of the owning struct, instead of managing their lifetimes manually and potentially having objects on the GPU/in the driver live forever.



However, all the functions in the destroy family of functions take the type directly, rather than a reference, so when I try to pass them from my structs, I get errors like the following:



error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
--> src/lib.rs:9:18
|
9 | destroyT(self.member)
| ^^^^^^^^^^^ cannot move out of here


It seems like there should be some way around this issue, as I'm currently in the Drop::drop function itself, so self is already "consumed." How do I get the instances of these types out of self as T, and not &T?



struct T;

struct S {
member: T,
}

impl Drop for S {
fn drop(&mut self) {
destroyT(self.member)
}
}

// elsewhere, in a library

fn destroyT(t: T) {
//...
}









share|improve this question















I'm using gfx-hal, which requires me to create resources which need to be explicitly destroyed using functions specific to their type. I'd like to store instances of these types in structs, and I'd also like to tie cleaning them up to the lifetime of the owning struct, instead of managing their lifetimes manually and potentially having objects on the GPU/in the driver live forever.



However, all the functions in the destroy family of functions take the type directly, rather than a reference, so when I try to pass them from my structs, I get errors like the following:



error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
--> src/lib.rs:9:18
|
9 | destroyT(self.member)
| ^^^^^^^^^^^ cannot move out of here


It seems like there should be some way around this issue, as I'm currently in the Drop::drop function itself, so self is already "consumed." How do I get the instances of these types out of self as T, and not &T?



struct T;

struct S {
member: T,
}

impl Drop for S {
fn drop(&mut self) {
destroyT(self.member)
}
}

// elsewhere, in a library

fn destroyT(t: T) {
//...
}






rust






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 12 at 1:40









Shepmaster

145k11279413




145k11279413










asked Nov 12 at 0:33









Ben Pious

3,80611526




3,80611526








  • 2




    Looks like you're not the only one to find this frustrating: github.com/gfx-rs/gfx/issues/2452
    – loganfsmyth
    Nov 12 at 2:07










  • Couldn't you use a NewType for T that implements Drop and calls destroy(). That way, the Drop for S would be automatically generated.
    – rodrigo
    Nov 12 at 10:29






  • 1




    @rodrigo Isn't that exactly what the OP was trying to do?
    – Sven Marnach
    Nov 12 at 10:52










  • @SvenMarnach: Oh, I see. I was assuming S was some composite type. But then the NewType would be exactly like this S.
    – rodrigo
    Nov 12 at 11:02














  • 2




    Looks like you're not the only one to find this frustrating: github.com/gfx-rs/gfx/issues/2452
    – loganfsmyth
    Nov 12 at 2:07










  • Couldn't you use a NewType for T that implements Drop and calls destroy(). That way, the Drop for S would be automatically generated.
    – rodrigo
    Nov 12 at 10:29






  • 1




    @rodrigo Isn't that exactly what the OP was trying to do?
    – Sven Marnach
    Nov 12 at 10:52










  • @SvenMarnach: Oh, I see. I was assuming S was some composite type. But then the NewType would be exactly like this S.
    – rodrigo
    Nov 12 at 11:02








2




2




Looks like you're not the only one to find this frustrating: github.com/gfx-rs/gfx/issues/2452
– loganfsmyth
Nov 12 at 2:07




Looks like you're not the only one to find this frustrating: github.com/gfx-rs/gfx/issues/2452
– loganfsmyth
Nov 12 at 2:07












Couldn't you use a NewType for T that implements Drop and calls destroy(). That way, the Drop for S would be automatically generated.
– rodrigo
Nov 12 at 10:29




Couldn't you use a NewType for T that implements Drop and calls destroy(). That way, the Drop for S would be automatically generated.
– rodrigo
Nov 12 at 10:29




1




1




@rodrigo Isn't that exactly what the OP was trying to do?
– Sven Marnach
Nov 12 at 10:52




@rodrigo Isn't that exactly what the OP was trying to do?
– Sven Marnach
Nov 12 at 10:52












@SvenMarnach: Oh, I see. I was assuming S was some composite type. But then the NewType would be exactly like this S.
– rodrigo
Nov 12 at 11:02




@SvenMarnach: Oh, I see. I was assuming S was some composite type. But then the NewType would be exactly like this S.
– rodrigo
Nov 12 at 11:02












1 Answer
1






active

oldest

votes

















up vote
4
down vote



accepted










The safest, easiest way to do this is to use an Option:



struct T;

impl Drop for T {
fn drop(&mut self) {
println!("dropping T");
}
}

struct S {
member: Option<T>,
}

impl Drop for S {
fn drop(&mut self) {
if let Some(t) = self.member.take() {
destroy_t(t);
}
}
}

fn destroy_t(_t: T) {
println!("destroy T");
}

fn main() {
let _x = S { member: Some(T) };
}




You could choose to use unsafe code with ManuallyDrop and swap out the current value for an uninitialized one1:



use std::mem::{self, ManuallyDrop};

struct T;

impl Drop for T {
fn drop(&mut self) {
println!("dropping T");
}
}

struct S {
member: ManuallyDrop<T>,
}

impl Drop for S {
fn drop(&mut self) {
unsafe {
let valid_t = mem::replace(&mut *self.member, mem::uninitialized());
destroy_t(valid_t);
// do *not* call ManuallyDrop::drop
};
}
}

fn destroy_t(_t: T) {
println!("destroy T");
}

fn main() {
let _x = S {
member: ManuallyDrop::new(T),
};
}


1 Using mem::uninitialized is extremely dangerous and hard to get right, especially in generic contexts. Using the nightly MaybeUninit, this might look like



#![feature(maybe_uninit)]

use std::mem::{self, ManuallyDrop, MaybeUninit};

struct T;

impl Drop for T {
fn drop(&mut self) {
println!("dropping T");
}
}

struct S {
member: ManuallyDrop<MaybeUninit<T>>,
}

impl Drop for S {
fn drop(&mut self) {
let invalid_t = MaybeUninit::uninitialized();
let valid_t = mem::replace(&mut *self.member, invalid_t);
let valid_t = unsafe { valid_t.into_inner() };
destroy_t(valid_t);
// do *not* call ManuallyDrop::drop
}
}

fn destroy_t(_t: T) {
println!("destroy T");
}

fn main() {
let _x = S {
member: ManuallyDrop::new(MaybeUninit::new(T)),
};
}


See also:




  • How to move one field out of a struct that implements Drop trait?






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53254645%2fhow-can-i-move-a-value-out-of-the-argument-to-dropdrop%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    4
    down vote



    accepted










    The safest, easiest way to do this is to use an Option:



    struct T;

    impl Drop for T {
    fn drop(&mut self) {
    println!("dropping T");
    }
    }

    struct S {
    member: Option<T>,
    }

    impl Drop for S {
    fn drop(&mut self) {
    if let Some(t) = self.member.take() {
    destroy_t(t);
    }
    }
    }

    fn destroy_t(_t: T) {
    println!("destroy T");
    }

    fn main() {
    let _x = S { member: Some(T) };
    }




    You could choose to use unsafe code with ManuallyDrop and swap out the current value for an uninitialized one1:



    use std::mem::{self, ManuallyDrop};

    struct T;

    impl Drop for T {
    fn drop(&mut self) {
    println!("dropping T");
    }
    }

    struct S {
    member: ManuallyDrop<T>,
    }

    impl Drop for S {
    fn drop(&mut self) {
    unsafe {
    let valid_t = mem::replace(&mut *self.member, mem::uninitialized());
    destroy_t(valid_t);
    // do *not* call ManuallyDrop::drop
    };
    }
    }

    fn destroy_t(_t: T) {
    println!("destroy T");
    }

    fn main() {
    let _x = S {
    member: ManuallyDrop::new(T),
    };
    }


    1 Using mem::uninitialized is extremely dangerous and hard to get right, especially in generic contexts. Using the nightly MaybeUninit, this might look like



    #![feature(maybe_uninit)]

    use std::mem::{self, ManuallyDrop, MaybeUninit};

    struct T;

    impl Drop for T {
    fn drop(&mut self) {
    println!("dropping T");
    }
    }

    struct S {
    member: ManuallyDrop<MaybeUninit<T>>,
    }

    impl Drop for S {
    fn drop(&mut self) {
    let invalid_t = MaybeUninit::uninitialized();
    let valid_t = mem::replace(&mut *self.member, invalid_t);
    let valid_t = unsafe { valid_t.into_inner() };
    destroy_t(valid_t);
    // do *not* call ManuallyDrop::drop
    }
    }

    fn destroy_t(_t: T) {
    println!("destroy T");
    }

    fn main() {
    let _x = S {
    member: ManuallyDrop::new(MaybeUninit::new(T)),
    };
    }


    See also:




    • How to move one field out of a struct that implements Drop trait?






    share|improve this answer



























      up vote
      4
      down vote



      accepted










      The safest, easiest way to do this is to use an Option:



      struct T;

      impl Drop for T {
      fn drop(&mut self) {
      println!("dropping T");
      }
      }

      struct S {
      member: Option<T>,
      }

      impl Drop for S {
      fn drop(&mut self) {
      if let Some(t) = self.member.take() {
      destroy_t(t);
      }
      }
      }

      fn destroy_t(_t: T) {
      println!("destroy T");
      }

      fn main() {
      let _x = S { member: Some(T) };
      }




      You could choose to use unsafe code with ManuallyDrop and swap out the current value for an uninitialized one1:



      use std::mem::{self, ManuallyDrop};

      struct T;

      impl Drop for T {
      fn drop(&mut self) {
      println!("dropping T");
      }
      }

      struct S {
      member: ManuallyDrop<T>,
      }

      impl Drop for S {
      fn drop(&mut self) {
      unsafe {
      let valid_t = mem::replace(&mut *self.member, mem::uninitialized());
      destroy_t(valid_t);
      // do *not* call ManuallyDrop::drop
      };
      }
      }

      fn destroy_t(_t: T) {
      println!("destroy T");
      }

      fn main() {
      let _x = S {
      member: ManuallyDrop::new(T),
      };
      }


      1 Using mem::uninitialized is extremely dangerous and hard to get right, especially in generic contexts. Using the nightly MaybeUninit, this might look like



      #![feature(maybe_uninit)]

      use std::mem::{self, ManuallyDrop, MaybeUninit};

      struct T;

      impl Drop for T {
      fn drop(&mut self) {
      println!("dropping T");
      }
      }

      struct S {
      member: ManuallyDrop<MaybeUninit<T>>,
      }

      impl Drop for S {
      fn drop(&mut self) {
      let invalid_t = MaybeUninit::uninitialized();
      let valid_t = mem::replace(&mut *self.member, invalid_t);
      let valid_t = unsafe { valid_t.into_inner() };
      destroy_t(valid_t);
      // do *not* call ManuallyDrop::drop
      }
      }

      fn destroy_t(_t: T) {
      println!("destroy T");
      }

      fn main() {
      let _x = S {
      member: ManuallyDrop::new(MaybeUninit::new(T)),
      };
      }


      See also:




      • How to move one field out of a struct that implements Drop trait?






      share|improve this answer

























        up vote
        4
        down vote



        accepted







        up vote
        4
        down vote



        accepted






        The safest, easiest way to do this is to use an Option:



        struct T;

        impl Drop for T {
        fn drop(&mut self) {
        println!("dropping T");
        }
        }

        struct S {
        member: Option<T>,
        }

        impl Drop for S {
        fn drop(&mut self) {
        if let Some(t) = self.member.take() {
        destroy_t(t);
        }
        }
        }

        fn destroy_t(_t: T) {
        println!("destroy T");
        }

        fn main() {
        let _x = S { member: Some(T) };
        }




        You could choose to use unsafe code with ManuallyDrop and swap out the current value for an uninitialized one1:



        use std::mem::{self, ManuallyDrop};

        struct T;

        impl Drop for T {
        fn drop(&mut self) {
        println!("dropping T");
        }
        }

        struct S {
        member: ManuallyDrop<T>,
        }

        impl Drop for S {
        fn drop(&mut self) {
        unsafe {
        let valid_t = mem::replace(&mut *self.member, mem::uninitialized());
        destroy_t(valid_t);
        // do *not* call ManuallyDrop::drop
        };
        }
        }

        fn destroy_t(_t: T) {
        println!("destroy T");
        }

        fn main() {
        let _x = S {
        member: ManuallyDrop::new(T),
        };
        }


        1 Using mem::uninitialized is extremely dangerous and hard to get right, especially in generic contexts. Using the nightly MaybeUninit, this might look like



        #![feature(maybe_uninit)]

        use std::mem::{self, ManuallyDrop, MaybeUninit};

        struct T;

        impl Drop for T {
        fn drop(&mut self) {
        println!("dropping T");
        }
        }

        struct S {
        member: ManuallyDrop<MaybeUninit<T>>,
        }

        impl Drop for S {
        fn drop(&mut self) {
        let invalid_t = MaybeUninit::uninitialized();
        let valid_t = mem::replace(&mut *self.member, invalid_t);
        let valid_t = unsafe { valid_t.into_inner() };
        destroy_t(valid_t);
        // do *not* call ManuallyDrop::drop
        }
        }

        fn destroy_t(_t: T) {
        println!("destroy T");
        }

        fn main() {
        let _x = S {
        member: ManuallyDrop::new(MaybeUninit::new(T)),
        };
        }


        See also:




        • How to move one field out of a struct that implements Drop trait?






        share|improve this answer














        The safest, easiest way to do this is to use an Option:



        struct T;

        impl Drop for T {
        fn drop(&mut self) {
        println!("dropping T");
        }
        }

        struct S {
        member: Option<T>,
        }

        impl Drop for S {
        fn drop(&mut self) {
        if let Some(t) = self.member.take() {
        destroy_t(t);
        }
        }
        }

        fn destroy_t(_t: T) {
        println!("destroy T");
        }

        fn main() {
        let _x = S { member: Some(T) };
        }




        You could choose to use unsafe code with ManuallyDrop and swap out the current value for an uninitialized one1:



        use std::mem::{self, ManuallyDrop};

        struct T;

        impl Drop for T {
        fn drop(&mut self) {
        println!("dropping T");
        }
        }

        struct S {
        member: ManuallyDrop<T>,
        }

        impl Drop for S {
        fn drop(&mut self) {
        unsafe {
        let valid_t = mem::replace(&mut *self.member, mem::uninitialized());
        destroy_t(valid_t);
        // do *not* call ManuallyDrop::drop
        };
        }
        }

        fn destroy_t(_t: T) {
        println!("destroy T");
        }

        fn main() {
        let _x = S {
        member: ManuallyDrop::new(T),
        };
        }


        1 Using mem::uninitialized is extremely dangerous and hard to get right, especially in generic contexts. Using the nightly MaybeUninit, this might look like



        #![feature(maybe_uninit)]

        use std::mem::{self, ManuallyDrop, MaybeUninit};

        struct T;

        impl Drop for T {
        fn drop(&mut self) {
        println!("dropping T");
        }
        }

        struct S {
        member: ManuallyDrop<MaybeUninit<T>>,
        }

        impl Drop for S {
        fn drop(&mut self) {
        let invalid_t = MaybeUninit::uninitialized();
        let valid_t = mem::replace(&mut *self.member, invalid_t);
        let valid_t = unsafe { valid_t.into_inner() };
        destroy_t(valid_t);
        // do *not* call ManuallyDrop::drop
        }
        }

        fn destroy_t(_t: T) {
        println!("destroy T");
        }

        fn main() {
        let _x = S {
        member: ManuallyDrop::new(MaybeUninit::new(T)),
        };
        }


        See also:




        • How to move one field out of a struct that implements Drop trait?







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 12 at 2:13

























        answered Nov 12 at 2:01









        Shepmaster

        145k11279413




        145k11279413






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53254645%2fhow-can-i-move-a-value-out-of-the-argument-to-dropdrop%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Bressuire

            Vorschmack

            Quarantine