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

时间:2016-07-15 13:17:55

标签: rust move-semantics boxing ownership-semantics

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

type Foo = Vec<u8>;

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

现在考虑一种在某种程度上与Box非常相似的类型:

struct NotBox<T> {
    contents: T
}

我们可以编写一个临时移出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
    }
}

我想编写一个与Box es兼容的类似函数,但编译器不喜欢它:

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
    }
}

我可以返回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
    })
}

可以解决这个问题吗?

3 个答案:

答案 0 :(得分:9)

所以,离开Box是一个特例......现在是什么?

std::mem模块提供了许多安全功能来移动值,而不会将漏洞(!)放入Rust的内存安全性中。这里感兴趣的是swapreplace

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

我们可以这样使用:

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
    }
}

它也有助于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 })
}

答案 1 :(得分:4)

移出盒子在编译器中是特殊的。你可以从中移出一些东西,但是你无法移回一些东西,因为搬出的行为也会解除分配。您可以使用std::ptr::writestd::ptr::readstd::ptr::replace做一些愚蠢的事情,但很难做到正确,因为某些有效应该在{{1}内当它被删除。我建议只接受分配,或改为转换为Box

答案 2 :(得分:0)

  

我们可以编写一个暂时移出NotBox内容的函数,并在返回之前重新放入内容

那是因为你可以部分地从按价值的结构中移出。它表现得好像所有字段都是单独的变量。如果结构实现Drop,那是不可能的,因为drop需要整个结构有效,总是(如果发生恐慌)。

至于提供解决方法,您还没有提供足够的信息 - 尤其是,为什么baz需要Box作为参数以及为什么quux无法解决?您可以使用哪些功能以及哪些功能是您无法更改的API的一部分? Foo的真实类型是什么?它太大了吗?

最好的解决方法是根本不使用Box