Welcome to ZaiNaLe Developer Community-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
665 views
in Technique[技术] by (71.8m points)

rust - How can I reuse a box that I have moved the value out of?

I have some non-copyable type and a function that consumes and (maybe) produces it:

type Foo = Vec<u8>;

fn quux(_: Foo) -> Option<Foo> {
    Some(Vec::new())
}

Now consider a type that is somehow conceptually very similar to Box:

struct NotBox<T> {
    contents: T
}

We can write a function that temporarily moves out contents of the NotBox and puts something back in before returning it:

fn bar(mut notbox: NotBox<Foo>) -> Option<NotBox<Foo>> {
    let foo = notbox.contents; // now `notbox` is "empty"
    match quux(foo) {
        Some(new_foo) => {
            notbox.contents = new_foo; // we put something back in
            Some(notbox)
        }
        None => None
    }
}

I want to write an analogous function that works with Boxes but the compiler does not like it:

fn baz(mut abox: Box<Foo>) -> Option<Box<Foo>> {
    let foo = *abox; // now `abox` is "empty"
    match quux(foo) {
        Some(new_foo) => {
            *abox = new_foo; // error: use of moved value: `abox`
            Some(abox)
        }
        None => None
    }
}

I could return Some(Box::new(new_foo)) instead but that performs unnecessary allocation - I already have some memory at my disposal! Is it possible to avoid that?

I would also like to get rid of the match statements but again the compiler is not happy with it (even for the NotBox version):

fn bar(mut notbox: NotBox<Foo>) -> Option<NotBox<Foo>> {
    let foo = notbox.contents;
    quux(foo).map(|new_foo| {
        notbox.contents = new_foo; // error: capture of partially moved value: `notbox`
        notbox
    })
}

Is it possible to work around that?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

So, moving out of a Box is a special case... now what?

The std::mem module presents a number of safe functions to move values around, without poking holes (!) into the memory safety of Rust. Of interest here are swap and replace:

pub fn replace<T>(dest: &mut T, src: T) -> T

Which we can use like so:

fn baz(mut abox: Box<Foo>) -> Option<Box<Foo>> {
    let foo = std::mem::replace(&mut *abox, Foo::default());

    match quux(foo) {
        Some(new_foo) => {
            *abox = new_foo;
            Some(abox)
        }
        None => None
    }
}

It also helps in the map case, because it does not borrow the Box:

fn baz(mut abox: Box<Foo>) -> Option<Box<Foo>> {
    let foo = std::mem::replace(&mut *abox, Foo::default());

    quux(foo).map(|new_foo| { *abox = new_foo; abox })
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to ZaiNaLe Developer Community-Open, Learning and Share
...