简体   繁体   English

具有内部可变性的Vec

[英]Vec with interior mutability

I have a struct AppData with contains a Vec<Box<Updatable>> called objects , which contains structs that implement a trait Updatable with the following function: 我有一个结构AppData ,其中包含一个称为objectsVec<Box<Updatable>> ,其中包含通过以下功能实现Updatable特征的结构:

fn update(&mut self, data: &mut AppData) {  
    //default implementation accesses and mutates AppData, including the internal `objects` Vec and possibly also objects inside it
}

The AppData struct is stored in a field data in a struct App with the following function: AppData结构通过以下功能存储在结构App中的字段data中:

pub fn update(&mut self) {

    for d in self.data.objects.iter(){
        d.update(&mut self.data);
    }

}

I cannot do this beacuse Box<T> is immutable. 我不能这样做,因为Box<T>是不可变的。 So I tried using indexers instead: 所以我尝试改用索引器:

for i in 0..self.data.objects.len() {
    let ref mut d = self.data.objects[i];
    d.update(&mut self.data);
}

But then I get 但后来我明白了

cannot borrow self.data as mutable more than once at a time 一次不能多次借用self.data作为可变的

So what do I do? 那我该怎么办? I could probably get it to compile using combinations of RefCell etc but I'm not sure it would be idiomatic Rust. 我可能可以使用RefCell等的组合来编译它,但是我不确定它是否是惯用的Rust。 A few alternatives: 一些替代方案:

  • Cloning the Vec and iterating over the clone instead. 克隆Vec并遍历克隆。 But I ran into trouble because Updateable does not implement Sized . 但是我遇到了麻烦,因为Updateable没有实现Sized
  • Using RefCell instead of Box . 使用RefCell代替Box I'm not sure I should need it since I'm not storing the references to the Vec inside the Updatables but that might not make a difference? 我不确定我是否需要它,因为我没有在Updatables存储对Vec的引用,但这可能没有什么不同? I suppose RefCell is supposed to be used over Rc in this scenario because I want mutable references? 我想在这种情况下应该在Rc上使用RefCell ,因为我想要可变的引用? Also this does not solve my problem because I still need to take ownership of self.data somehow, right? 但这也不能解决我的问题,因为我仍然需要以某种方式获取self.data所有权,对吗?
  • Taking ownership of self.data after deconstructing self and then placing it back into self after we are done with it. 在解构self之后获取self.data所有权,然后在完成后将其放回self。 How do I do that? 我怎么做?

Thanks in advance! 提前致谢!

You can use iter_mut() instead of iter() to get the same result as your indexer-based solution: 您可以使用iter_mut()而不是iter()获得与基于索引器的解决方案相同的结果:

pub fn update(&mut self) {
    for d in self.data.objects.iter_mut() {
        d.update(&mut self.data);
    }
}

(Yes, "same result" means we still get "cannot borrow self.data as mutable more than once at a time".) (是的,“相同的结果”表示我们仍然得到“一次不能多次借用self.data作为可变self.data ”。)

There are a couple of soundness issues in your program. 您的程序中存在几个健全性问题。 First, by passing a &mut AppData to Updatable::update() , the implementation of update() could destroy self by removing the corresponding item from objects ! 首先,通过传递&mut AppDataUpdatable::update() ,执行update()可能会破坏self通过删除相应的项目objects (It doesn't matter if AppData doesn't actually provide a method to do that.) AppData是否实际上没有提供执行此操作的方法都没有关系。)

Additionally, Updatable::update() could invalidate the iterator in App::update() by adding any item to or removing any item from objects . 另外,通过向objects添加任何项或从objects删除任何项, Updatable::update()可以使App::update()的迭代器无效。 Switching to an indexer-based loop only makes the problem worse, because your program might compile, but it will be buggy! 切换到基于索引器的循环只会使问题变得更糟,因为您的程序可能会编译,但是会出现问题!

In order to ensure that your Updatable remains alive for the duration of the update() call, you need to wrap it in some other smart pointer. 为了确保您的Updatableupdate()调用期间保持活动状态,您需要将其包装在其他一些智能指针中。 For example, you could wrap it in an Rc instead of a Box . 例如,您可以将其包装在Rc而不是Box As Rc doesn't let you take a mutable borrow to its contents, you may want to combine this with RefCell , like so: 由于Rc不允许您对其内容进行可变借用,因此您可能需要将此与RefCell结合使用,如下所示:

struct AppData {
    objects: Vec<Rc<RefCell<Updatable>>>,
}

We can do the same for the whole Vec : 我们可以对整个Vec做同样的事情:

struct AppData {
    objects: Rc<RefCell<Vec<Rc<RefCell<Updatable>>>>>,
}

However, that comes with a restriction: while you're iterating on objects in App::update() , you will not be able to mutate objects from implementations of Updatable::update() . 但是,这带有一个限制:在App::update() objects进行迭代时,您将无法从Updatable::update()实现中更改objects If you try to do so, your program will panic, because you can't have more than one active mutable borrow on the same RefCell . 如果您尝试这样做,则您的程序将崩溃,因为在同一RefCell上不能有多个活动的可变借RefCell

If you need to be able to mutate objects from implementations of Updatable::update() , then you probably want App::update() to iterate on whatever objects contained when you start the loop. 如果您需要能够从Updatable::update()实现中更改objects ,那么您可能希望App::update()在启动循环时对包含的任何objects进行迭代。 The simple solution to this is to clone the Vec just before the loop (we don't need Rc<RefCell<Vec<...>>> for this). 解决此问题的简单方法是在循环之前clone VecRc<RefCell<Vec<...>>>我们不需要Rc<RefCell<Vec<...>>> )。

However, cloning a Vec every time (even when not necessary) might be expensive, so you may want to avoid doing so when it's not needed. 但是,每次(即使在不必要时)克隆Vec可能都是昂贵的,因此您可能希望避免在不需要时这样做。 Instead of cloning the Vec systematically, we can wrap the Vec in an Rc (but no RefCell this time!), then clone the Rc before borrowing the vector in App::update() . 无需系统地克隆Vec ,我们可以将Vec包裹在Rc (这次没有RefCell !),然后在借用App::update()的向量之前先克隆Rc In AppData , methods that want to mutate objects would use Rc::make_mut to clone the Vec (if necessary!) and obtain a mutable reference. AppData ,想要更改objects将使用Rc::make_mut克隆Vec (如有必要!)并获得可变引用。 If the mutation occurs while App::update() is active, this will clone the Vec , which leaves the original Vec alone so the iteration can continue. 如果在App::update()处于活动状态时发生了突变,这将克隆Vec ,而将原始Vec ,以便继续进行迭代。 However, if there are no actives clones of the Rc , then this will not do a clone, it'll just give you a mutable reference to the Vec , because it's safe to do so. 但是,如果没有Rc active克隆,则不会进行克隆,它只是为您提供对Vec的可变引用,因为这样做是安全的。

Box<T> is not intrinsically immutable. Box<T> 不是本质不变。 It plays by the same rules of inherited mutability as most other types. 它与大多数其他类型一样,遵循继承的可变性规则。 Your problem is a combination of different issues. 您的问题是不同问题的组合。 First, .iter() gives an iterator over (immutable) references. 首先, .iter()为(不可变的)引用提供一个迭代器。 So even if you didn't need to borrow self.data mutably while iterating over it, you'll get an error for that. 因此,即使您不需要在迭代过程中可变地借用self.data ,您也会因此而出错。 If you want to iterate over mutable references, just do for d in &mut self.data.objects { ... } instead of the indexing dance. 如果要遍历可变引用,只需for d in &mut self.data.objects { ... }执行for d in &mut self.data.objects { ... }而不要执行索引操作。

Second, as you noticed, there is an issue with borrowing self.data while iterating over it. 其次,正如您所注意到的,在迭代self.data时存在一个问题。 This is a potential issue in your design. 这是设计中的潜在问题。 For example, what should happen if update removes an object from the objects vector? 例如,如果updateobjects向量中删除一个对象,应该怎么办?

There is no easy one-size-fits-all solution to this. 对此没有简单的一刀切解决方案。 Maybe RefCell<Box<Trait>> will help, maybe it would be terrible design. 也许RefCell<Box<Trait>>会有所帮助,也许这将是一个糟糕的设计。 Maybe update doesn't need the objects part of self.data and you can swap it out while iterating, which would prevent mutable aliasing. 也许update不需要self.dataobjects部分,并且您可以在迭代时将其交换出来,这将防止可变别名。 Maybe it's best to scrap the trait and pursue a wholly different design (it looks like you're trying to apply textbook OOP design which in my experience rarely works well in Rust). 也许最好是放弃这种特性并追求完全不同的设计(看来您正在尝试应用教科书OOP设计,根据我的经验,这种设计在Rust中很少能很好地发挥作用)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM