Move field out of struct without providing default?

I know take can cleanly move a field, replacing it with it's Default implementation

and I know replace can cleanly move a field, with you providing a new value to backfill it with.

But what option exists to just drop the struct, taking the only field I care about from it? This would be handy in situations where Default can't be implemented, and building a garbage backfill value is a bit involved (with a giant builder, etc.)

Thanks if the only answer is just replace

If there's no Drop implementation, you can just move the field out.

let local = struct.field;
// Or if you really wanted the other fields to drop *right now*
let local = {
    let struct = struct;
    struct.field
};

If there is a Drop implementation, you can't do it without take or replace or similar, because Drop would then receive a &mut _ to a partially uninitialized (deinitialized) structure.

1 Like

As @quinedot wrote, if there is no Drop implementation for the struct, something simple like this works:

impl MyStruct {
    pub fn into_some_field(self) -> SomeNoDefaultType {
        self.some_field
    }
}

However, if MyStruct has an Drop implementation, you can always make any type a default-able type by wrapping into an Option:

struct MyStruct {
    some_field: Option<SomeNoDefaultType>,
}

impl Drop for MyStruct {
    ...
}

impl MyStruct {
    pub fn into_some_field(self) -> SomeNoDefaultType {
        self.some_field.take()
    }
}

This can be a little bit annoying, because of course, all other location accessing some_field have to use unwrap() for the Option.

If this an issue, there is always the mighty unsafe-way:

struct MyStruct {
    some_field: SomeNoDefaultType,
    other_field: String,
}

impl Drop for MyStruct {
    ...
}

impl MyStruct {
    fn into_some_field(self) -> SomeNoDefaultType {
        let mut this = std::mem::MaybeUninit::new(self);
        let this = this.as_mut_ptr();
        
        unsafe {
            std::ptr::drop_in_place(&raw mut (*this).other_field);
            std::ptr::read(&raw const (*this).some_field)
        }
    }
}

But, yeah, that's of course unsafe. Might be wise, to use miri if you playing with these things...

1 Like

Do you want destructuring patterns?

struct BigStruct {
    foo: Foo,
    bar: Bar,
    // a hundred other fields...
}

fn takes_big_struct(big: BigStruct) {
    let BigStruct { foo, .. } = big;

    println!("foo: {foo:?}");
}
3 Likes