2016-07-15 70 views
8

我有一些不可複製的類型和消耗以及(也許)產生它的功能:如何重新使用我已將值移出的框?

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

你似乎要求超過o ne問題在這裏。比賽vs地圖看起來應該是移動到一個新的問題。 –

+0

我打算回答第一部分,但意識到我還不知道如何移出「Box '作品;它似乎並不涉及'Der​​ef'或'DerefMut'特徵。所以期待一個很好的答案! –

+0

@ChrisEmerson當我試圖創建我的問題的最小例子時,匹配部分就彈出來了,所以我沒有對它進行太多的研究。我只是認爲這可能與一般性問題有關,而且我沒有看到「部分」移動的工作方式,所以我把它留在了這裏。 – mrhania

回答

8

因此,搬出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 }) 
} 
+2

缺點是需要存在'Foo:default()',並且希望執行起來很便宜,如果兩者都不正確,那麼建議切換到「Box >'將是有用的,因爲你可以調用'take'和'None'是廉價創建的默認值。 – Shepmaster

+0

@Shepmaster:同意:)但是我寧願堅持提出的場景,只有在OP有更具體的關注時纔會詳細說明,否則恐怕答案可能會淹沒任何人的企圖讀它。 –

4

移出盒子是編譯器中的特例。你可以移出某些東西,但不能移回某些東西,因爲移出的行爲也會釋放。你可以用std::ptr::write,std::ptr::readstd::ptr::replace來做一些傻事,但很難做到這一點,因爲有效的東西應該在Box之內,當它被丟棄時。我建議只接受分配,或改用Box<Option<Foo>>

+1

我最大的煩惱就是像這樣的一些細節顯然沒有記錄在討論主題之外或有時在RFC中傳遞。 :-(FWIW,它被提及[在這個推遲的RFC中](https://github.com/rust-lang/rfcs/pull/178) –

+2

@ChrisEmerson我認爲在這種情況下,Box的特殊外殼是一些想要被移除並轉化爲建議的DerefMove特性的東西,不想顯示太多髒衣服,並且如果它可以變得更加可重用和通用,它就會被widley內化,注意[RFC已經被重新啓動] (https://github.com/rust-lang/rfcs/pull/1646) – Shepmaster

0

我們可以寫一個暫時移出NotBox的內容,並將其返回

那是因爲你可部分地從你走的價值結構遷出之前把東西回來功能。它的行爲就好像所有字段都是單獨的變量。這是不可能的,但如果結構實現了Drop,因爲drop需要整個結構有效,總是(在發生恐慌的情況下)。

至於提供解決方法,您沒有提供足夠的信息 - 特別是,爲什麼baz需要採取Box作爲參數,爲什麼quux不能?哪些功能是你的,哪些是你無法更改的API的一部分? Foo的真實類型是什麼?大麼?

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

相關問題