我的代码可变地借用了对
Application
的引用,但似乎不明白借用应该只持续到函数的持续时间,因为它抱怨第二个可变借用。我怎么能说我只希望借用在函数调用期间持续?
use std::error::Error;
pub trait Plugin {
fn initialize(&mut self, application: &mut Application) -> Result<(), Box<dyn Error>>;
}
pub struct Application {
plugins: Vec<Box<dyn Plugin>>,
}
impl Application {
pub fn run(&mut self) -> Result<(), Box<dyn Error>> {
for plugin in self.plugins.iter_mut() {
plugin.initialize(self);
}
Ok(())
}
}
| for plugin in self.plugins.iter_mut() {
| -----------------------
| |
| first mutable borrow occurs here
| first borrow later used here
| plugin.initialize(self);
| ^^^^ second mutable borrow occurs here
但似乎不明白借用应该只持续到函数的持续时间
但是借用被扩展了,因为
self.plugins.iter_mut()
返回的迭代器继续持有对 self
的独占借用。这是必要的,否则您可以调用 self.plugins.clear()
并使您的迭代器无效。只要迭代器继续存在,self
就被认为是完全借用的。
在这种情况下,解决该问题的一种方法是采用
self.plugins
并在循环后将其放回原位。由于 Vec
将元素存储在堆上,因此具有 O(1) 时间复杂度;指针只是被复制。
另请注意,您忘记处理
Result
中的 plugin.initialize()
。由于我们正在服用self.plugins
,因此我们需要注意完成后将其放回原位;如果发生错误,只需使用 ?
运算符将导致 self.plugins
为空。
pub fn run(&mut self) -> Result<(), Box<dyn Error>> {
let mut r = Ok(());
let mut plugins = std::mem::take(&mut self.plugins);
for plugin in plugins.iter_mut() {
r = plugin.initialize(self);
if r.is_err() { break; }
}
self.plugins = plugins;
r
}
如果这种模式在你的代码中经常出现,你可以将它封装在一个私有方法后面。这也将简化错误处理:
pub fn run(&mut self) -> Result<(), Box<dyn Error>> {
self.with_plugins(|s, plugins| {
for plugin in plugins.iter_mut() {
plugin.initialize(s)?;
}
Ok(())
})
}
fn with_plugins<F, T, E>(&mut self, f: F) -> Result<T, E>
where F: FnOnce(&mut Self, &mut Vec<Box<dyn Plugin>>) -> Result<T, E>
{
let mut plugins = std::mem::take(&mut self.plugins);
let r = f(self, &mut plugins);
self.plugins = plugins;
r
}
如果你想更加安全,你甚至可以在将
self.plugins
放回去之前断言 plugins
是空的;如果不是,则表明有什么东西试图在不应该改变它的时候改变它。
fn with_plugins<F, T, E>(&mut self, f: F) -> Result<T, E>
where F: FnOnce(&mut Self, &mut Vec<Box<dyn Plugin>>) -> Result<T, E>
{
let mut plugins = std::mem::take(&mut self.plugins);
let r = f(self, &mut plugins);
assert!(self.plugins.is_empty());
self.plugins = plugins;
r
}