11

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

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

我想编写一个与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
    }
}

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

有可能解决这个问题吗?

4

3 回答 3

13

所以,搬出 aBox是一个特例……现在呢?

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 })
}
于 2016-07-15T16:24:42.867 回答
4

开箱即用是编译器中的特殊情况。您可以从其中移出一些东西,但不能将某些东西移回其中,因为移出的行为也会解除分配。std::ptr::write你可以用, std::ptr::read, 和做一些傻事std::ptr::replace,但是很难把它做好,因为当它被丢弃时,一些有效的东西应该在里面。Box我建议只接受分配,或者改用 a Box<Option<Foo>>

于 2016-07-15T14:23:38.747 回答
0

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

那是因为您可以从按值获取的结构中部分移出。它的行为就像所有字段都是单独的变量一样。但是,如果结构实现了这是不可能的Drop,因为drop需要整个结构始终有效(以防恐慌)。

至于提供解决方法,您没有提供足够的信息——特别是,为什么baz需要Box作为论据,为什么quux不能?哪些功能是您的,哪些是您无法更改的 API 的一部分?真正的类型是Foo什么?很大吗?

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

于 2016-07-15T14:32:59.250 回答