如何重用我已移出值的框?

mrh*_*nia 9 boxing ownership-semantics move-semantics rust

我有一些不可复制的类型和一个消耗和(可能)生成它的函数:

type Foo = Vec<u8>;

fn quux(_: Foo) -> Option<Foo> {
    Some(Vec::new())
}
Run Code Online (Sandbox Code Playgroud)

现在考虑一种在概念上非常类似的类型Box:

struct NotBox<T> {
    contents: T
}
Run Code Online (Sandbox Code Playgroud)

我们可以编写一个临时移出内容的函数,NotBox并在返回之前放回一些东西:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

我想编写一个与Boxes 一起使用的类似函数,但编译器不喜欢它:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以返回Some(Box::new(new_foo))但是执行不必要的分配 - 我已经拥有了一些内存!有可能避免这种情况吗?

我也想摆脱这些match陈述,但编译器再次对它不满意(即使对于NotBox版本):

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
    })
}
Run Code Online (Sandbox Code Playgroud)

有可能解决这个问题吗?

Mat*_* M. 11

所以,搬出一个Box是一个特例......现在怎么办?

std::mem模块提供了许多安全功能来移动值,而不会在Rust的内存安全中戳孔(!).有趣的是这里有swapreplace:

pub fn replace<T>(dest: &mut T, src: T) -> T
Run Code Online (Sandbox Code Playgroud)

我们可以这样使用:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

它也有助于这种map情况,因为它没有借用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 })
}
Run Code Online (Sandbox Code Playgroud)

  • 缺点是`Foo:default()`需要存在并且希望执行起来很便宜.如果两者都不成立,那么切换到`Box <Option <Foo >>`的建议将是有用的,因为你可以调用`take`和`None`是便宜的创建默认值. (2认同)

小智 5

开箱即用是编译器中的特殊情况。您可以将某些内容移出其中,但无法将某些内容移回其中,因为移出的行为也会取消分配。您可以使用std::ptr::writestd::ptr::read、 和做一些愚蠢的事情std::ptr::replace,但很难做到正确,因为当 a 被删除时,有效的内容应该在 a 中。Box我建议只接受分配,或者改用 a Box<Option<Foo>>

  • @ChrisEmerson 我认为在这种情况下,“Box”的特殊外壳是需要删除并转换为建议的“DerefMove”特征的东西。不想展示太多脏衣服,如果它可以变得更加可重用和通用,那么就将其广泛内化。请注意,[RFC 也已重新启动](https://github.com/rust-lang/rfcs/pull/1646)。 (2认同)