2015-04-01 85 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

相关问题