如何只在函数调用期间进行借用

问题描述 投票:0回答:1

我的代码可变地借用了对

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
rust borrow-checker
1个回答
0
投票

但似乎不明白借用应该只持续到函数的持续时间

但是借用被扩展了,因为

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
}
© www.soinside.com 2019 - 2024. All rights reserved.