2015-04-01 78 views
8

我有一個內部可變性的結構。如何在不破壞封裝的情況下返回對RefCell內的某些內容的引用?

use std::cell::RefCell; 

struct MutableInterior { 
    hideMe: i32, 
    vec: Vec<i32>, 
} 
struct Foo { 
    //although not used in this particular snippet, 
    //the motivating problem uses interior mutability 
    //via RefCell. 
    interior: RefCell<MutableInterior>, 
} 

impl Foo { 
    pub fn getItems(&self) -> &Vec<i32> { 
     &self.interior.borrow().vec 
    } 
} 

fn main() { 
    let f = Foo { 
     interior: RefCell::new(MutableInterior { 
      vec: Vec::new(), 
      hideMe: 2, 
     }), 
    }; 
    let borrowed_f = &f; 
    let items = borrowed_f.getItems(); 
} 

產生錯誤:

error: borrowed value does not live long enough 
    --> src/main.rs:16:10 
    | 
16 |   &self.interior.borrow().vec 
    |   ^^^^^^^^^^^^^^^^^^^^^^ does not live long enough 
17 |  } 
    |  - temporary value only lives until here 
    | 
note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:40... 
    --> src/main.rs:15:41 
    | 
15 |  pub fn getItems(&self) -> &Vec<i32> { 
    | _________________________________________^ starting here... 
16 | |   &self.interior.borrow().vec 
17 | |  } 
    | |_____^ ...ending here 

的問題是,我不能有一個功能上Foo返回借來vec,因爲借來的vec是僅適用於Ref的壽命,但Ref立即超出範圍。

我覺得Ref必須堅持圍繞because

RefCell uses Rust's lifetimes to implement 'dynamic borrowing', a process whereby one can claim temporary, exclusive, mutable access to the inner value. Borrows for RefCells are tracked 'at runtime', unlike Rust's native reference types which are entirely tracked statically, at compile time. Because RefCell borrows are dynamic it is possible to attempt to borrow a value that is already mutably borrowed; when this happens it results in task panic.

現在我可以改爲寫這樣一個函數,返回整個內飾:

pub fn getMutableInterior(&self) -> std::cell::Ref<MutableInterior>; 

然而,這可能暴露出場(MutableInterior.hideMe在這個例子)是真正的私人執行細節到Foo

理想情況下,我只是想公開vec本身,可能帶有警衛以實現動態借用行爲。然後來電者不必知道hideMe

回答

14

您可以創建一個新的結構類似於RefCell::borrow()返回,爲了包裝這個Ref並避免它走出去的範圍,這樣的Ref<'a,T>後衛:

use std::cell::Ref; 

struct FooGuard<'a> { 
    guard: Ref<'a, MutableInterior> 
} 

然後,你可以實施Deref特質吧,這樣它可以被用作如果它是一個:

use std::ops::Deref; 

impl<'b> Deref for FooGuard<'b> { 
    type Target = Vec<i32>; 

    fn deref(&self) -> &Vec<i32> { 
     &self.guard.vec 
    } 
} 

後,SIM卡層更新getItems()方法返回一個FooGuard實例:

​​3210

Deref確實神奇:

fn main() { 
    let f = Foo { 
     interior: RefCell::new(MutableInterior{ vec: Vec::new(), hideMe: 2 }) 
    }; 
    let borrowed_f = &f; 
    let items = borrowed_f.getItems(); 
    let v: &Vec<i32> = &items; 
} 
+0

這是做到這一點的唯一/慣用的方法是什麼?似乎有點麻煩......雖然我認爲,而不是一個getItems()方法,你可以直接借用內部塊在那裏它會超出範圍(或...) – norcalli 2015-04-02 08:37:08

+2

@Norcalli在具體如果是'RefCell'的情況,當引用超出作用域時(這是'Ref'的析構函數的作用),該對象需要被通知。在這裏,我們需要保持這種行爲(OP的錯誤是由於'Ref'實例被放棄得太早),因此封裝了它。 – Levans 2015-04-02 08:40:43

相關問題