rust 如何使借用仅在函数调用期间持续

5kgi1eie  于 2023-04-06  发布在  其他
关注(0)|答案(1)|浏览(79)

我的代码可变地借用了对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
tpgth1q7

tpgth1q71#

但似乎不明白借用应该只持续函数的持续时间
但是借用是扩展的,因为self.plugins.iter_mut()返回的迭代器继续持有对self的独占借用。这是必要的,否则您可以调用self.plugins.clear()并使迭代器无效。只要迭代器继续存在,self就被认为是独占借用的。
在这种情况下,一种解决问题的方法是在循环后将self.plugins放回。由于Vec将元素存储在堆上,因此时间复杂度为O(1);指针只是被复制。
还要注意,您忘记处理来自plugin.initialize()Result。如果发生错误,简单地使用?运算符将导致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
}

如果您想更加安全,您甚至可以在放回plugins之前Assertself.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
}

相关问题