具有内部可变性的 Vec
Vec with interior mutability
我有一个结构 AppData
,其中包含一个名为 objects
的 Vec<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
}
AppData
结构存储在结构 App
的字段 data
中,具有以下功能:
pub fn update(&mut self) {
for d in self.data.objects.iter(){
d.update(&mut self.data);
}
}
我不能这样做,因为 Box<T>
是不可变的。所以我尝试改用索引器:
for i in 0..self.data.objects.len() {
let ref mut d = self.data.objects[i];
d.update(&mut self.data);
}
但后来我得到
cannot borrow self.data
as mutable more than once at a time
那我该怎么办?我可能可以使用 RefCell
等的组合来编译它,但我不确定它是否是惯用的 Rust。几个备选方案:
- 克隆
Vec
并迭代克隆。但是我 运行 遇到了麻烦,因为 Updateable
没有实现 Sized
.
- 使用
RefCell
而不是 Box
。我不确定我是否需要它,因为我没有将对 Vec
的引用存储在 Updatables
中,但这可能没有什么不同?我想 RefCell
应该在这种情况下用于 Rc
因为我想要可变引用?这也不能解决我的问题,因为我仍然需要以某种方式获得 self.data
的所有权,对吗?
- 在解构
self
后取得 self.data
的所有权,然后在完成后将其放回自我。我该怎么做?
提前致谢!
您可以使用 iter_mut()
而不是 iter()
以获得与基于索引器的解决方案相同的结果:
pub fn update(&mut self) {
for d in self.data.objects.iter_mut() {
d.update(&mut self.data);
}
}
(是的,"same result" 意味着我们仍然得到 "cannot borrow self.data
as mutable more than once at a time"。)
您的程序中存在一些健全性问题。首先,通过将 &mut AppData
传递给 Updatable::update()
,update()
的实现可以通过从 objects
中删除相应的项目来破坏 self
! (如果 AppData
实际上没有提供这样做的方法也没关系。)
此外,Updatable::update()
可以通过向 objects
添加或删除任何项目来使 App::update()
中的迭代器无效。切换到基于索引器的循环只会使问题变得更糟,因为您的程序可能会编译,但它会出现错误!
为了确保您的 Updatable
在 update()
调用期间保持活动状态,您需要将其包装在其他一些智能指针中。例如,您可以将其包装在 Rc
instead of a Box
. As Rc
doesn't let you take a mutable borrow to its contents, you may want to combine this with RefCell
中,如下所示:
struct AppData {
objects: Vec<Rc<RefCell<Updatable>>>,
}
我们可以对整个做同样的事情Vec
:
struct AppData {
objects: Rc<RefCell<Vec<Rc<RefCell<Updatable>>>>>,
}
但是,这有一个限制:当您在 App::update()
中迭代 objects
时,您将无法从 [=17= 的实现中改变 objects
].如果您尝试这样做,您的程序将会崩溃,因为您不能在同一个 RefCell
.
上拥有多个活动的可变借用。
如果您需要能够从 Updatable::update()
的实现中改变 objects
,那么您可能希望 App::update()
在启动时迭代包含的任何 objects
环形。对此的简单解决方案是在循环之前 clone
Vec
(我们不需要 Rc<RefCell<Vec<...>>>
)。
但是,每次(即使没有必要)克隆 Vec
可能代价高昂,因此您可能希望在不需要时避免这样做。我们可以将 Vec
包装在 Rc
中,而不是系统地克隆 Vec
(但这次没有 RefCell
!),然后在借用之前克隆 Rc
App::update()
中的向量。在 AppData
中,想要改变 objects
的方法将使用 Rc::make_mut
克隆 Vec
(如果需要!)并获得可变引用。如果在 App::update()
处于活动状态时发生突变,这将克隆 Vec
,从而使原始 Vec
单独存在,以便迭代可以继续。但是,如果没有 Rc
的活动克隆,那么这不会进行克隆,它只会为您提供对 Vec
的可变引用,因为这样做是安全的。
Box<T>
本质上是 而不是 不可变的。它遵循与大多数其他类型相同的继承可变性规则。您的问题是不同问题的组合。首先,.iter()
给出了(不可变)引用的迭代器。因此,即使您在遍历它时不需要可变地借用 self.data
,您也会因此得到一个错误。如果您想遍历可变引用,只需执行 for d in &mut self.data.objects { ... }
而不是索引舞蹈。
其次,正如您所注意到的,在迭代时借用 self.data
存在问题。 这是您设计中的一个潜在问题。例如,如果 update
从 objects
向量中删除一个对象会发生什么?
对此没有简单的一刀切的解决方案。也许 RefCell<Box<Trait>>
会有所帮助,也许这会是糟糕的设计。也许 update
不需要 self.data
的 objects
部分,您可以在迭代时将其换掉,这将防止可变别名。也许最好放弃这个特性并追求完全不同的设计(看起来你正在尝试应用教科书 OOP 设计,根据我的经验,这在 Rust 中很少有效)。
我有一个结构 AppData
,其中包含一个名为 objects
的 Vec<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
}
AppData
结构存储在结构 App
的字段 data
中,具有以下功能:
pub fn update(&mut self) {
for d in self.data.objects.iter(){
d.update(&mut self.data);
}
}
我不能这样做,因为 Box<T>
是不可变的。所以我尝试改用索引器:
for i in 0..self.data.objects.len() {
let ref mut d = self.data.objects[i];
d.update(&mut self.data);
}
但后来我得到
cannot borrow
self.data
as mutable more than once at a time
那我该怎么办?我可能可以使用 RefCell
等的组合来编译它,但我不确定它是否是惯用的 Rust。几个备选方案:
- 克隆
Vec
并迭代克隆。但是我 运行 遇到了麻烦,因为Updateable
没有实现Sized
. - 使用
RefCell
而不是Box
。我不确定我是否需要它,因为我没有将对Vec
的引用存储在Updatables
中,但这可能没有什么不同?我想RefCell
应该在这种情况下用于Rc
因为我想要可变引用?这也不能解决我的问题,因为我仍然需要以某种方式获得self.data
的所有权,对吗? - 在解构
self
后取得self.data
的所有权,然后在完成后将其放回自我。我该怎么做?
提前致谢!
您可以使用 iter_mut()
而不是 iter()
以获得与基于索引器的解决方案相同的结果:
pub fn update(&mut self) {
for d in self.data.objects.iter_mut() {
d.update(&mut self.data);
}
}
(是的,"same result" 意味着我们仍然得到 "cannot borrow self.data
as mutable more than once at a time"。)
您的程序中存在一些健全性问题。首先,通过将 &mut AppData
传递给 Updatable::update()
,update()
的实现可以通过从 objects
中删除相应的项目来破坏 self
! (如果 AppData
实际上没有提供这样做的方法也没关系。)
此外,Updatable::update()
可以通过向 objects
添加或删除任何项目来使 App::update()
中的迭代器无效。切换到基于索引器的循环只会使问题变得更糟,因为您的程序可能会编译,但它会出现错误!
为了确保您的 Updatable
在 update()
调用期间保持活动状态,您需要将其包装在其他一些智能指针中。例如,您可以将其包装在 Rc
instead of a Box
. As Rc
doesn't let you take a mutable borrow to its contents, you may want to combine this with RefCell
中,如下所示:
struct AppData {
objects: Vec<Rc<RefCell<Updatable>>>,
}
我们可以对整个做同样的事情Vec
:
struct AppData {
objects: Rc<RefCell<Vec<Rc<RefCell<Updatable>>>>>,
}
但是,这有一个限制:当您在 App::update()
中迭代 objects
时,您将无法从 [=17= 的实现中改变 objects
].如果您尝试这样做,您的程序将会崩溃,因为您不能在同一个 RefCell
.
如果您需要能够从 Updatable::update()
的实现中改变 objects
,那么您可能希望 App::update()
在启动时迭代包含的任何 objects
环形。对此的简单解决方案是在循环之前 clone
Vec
(我们不需要 Rc<RefCell<Vec<...>>>
)。
但是,每次(即使没有必要)克隆 Vec
可能代价高昂,因此您可能希望在不需要时避免这样做。我们可以将 Vec
包装在 Rc
中,而不是系统地克隆 Vec
(但这次没有 RefCell
!),然后在借用之前克隆 Rc
App::update()
中的向量。在 AppData
中,想要改变 objects
的方法将使用 Rc::make_mut
克隆 Vec
(如果需要!)并获得可变引用。如果在 App::update()
处于活动状态时发生突变,这将克隆 Vec
,从而使原始 Vec
单独存在,以便迭代可以继续。但是,如果没有 Rc
的活动克隆,那么这不会进行克隆,它只会为您提供对 Vec
的可变引用,因为这样做是安全的。
Box<T>
本质上是 而不是 不可变的。它遵循与大多数其他类型相同的继承可变性规则。您的问题是不同问题的组合。首先,.iter()
给出了(不可变)引用的迭代器。因此,即使您在遍历它时不需要可变地借用 self.data
,您也会因此得到一个错误。如果您想遍历可变引用,只需执行 for d in &mut self.data.objects { ... }
而不是索引舞蹈。
其次,正如您所注意到的,在迭代时借用 self.data
存在问题。 这是您设计中的一个潜在问题。例如,如果 update
从 objects
向量中删除一个对象会发生什么?
对此没有简单的一刀切的解决方案。也许 RefCell<Box<Trait>>
会有所帮助,也许这会是糟糕的设计。也许 update
不需要 self.data
的 objects
部分,您可以在迭代时将其换掉,这将防止可变别名。也许最好放弃这个特性并追求完全不同的设计(看起来你正在尝试应用教科书 OOP 设计,根据我的经验,这在 Rust 中很少有效)。