Rust 在匹配表达式中借用可变的 self

标签 rust

我在 match 表达式中借用 self 时遇到问题:

fn add_once(&'t mut self, p_name: &'t str) -> Box<Element> {
    match self.get(p_name) {
        Some(x) => Box::new(*x),
        None => self.add(p_name),
    }
}

get()add() 函数的签名是:

fn get(&self, p_name: &str) -> Option<&Element>
fn add(&'t mut self, p_name: &'t str) -> Box<Element>

编译器拒绝这段代码:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:38:21
   |
36 |         match self.get(p_name) {
   |               ---- immutable borrow occurs here
37 |             Some(x) => Box::new(*x),
38 |             None => self.add(p_name),
   |                     ^^^^ mutable borrow occurs here
39 |         }
40 |     }
   |     - immutable borrow ends here

我明白了,但我不知道如何重写 match 表达式。

related question 中,它是通过让 match 返回一个值然后调用该函数来解决的。但是,这在这里不起作用,因为条件的意思不是选择一个值,而是有选择地执行一个 Action 。

完整代码示例如下:

struct Element<'e> {
    name: &'e str, 
}

impl<'e> Element<'e> {
    fn new(p_name: &str) -> Element {
        Element { name: p_name }
    }
}

struct Top<'t> {
    list: Vec<Element<'t>>,
}

impl<'t> Top<'t> {
    fn new() -> Top<'t> {
        Top { list: vec![] }
    }

    fn get(&self, p_name: &str) -> Option<&Element> {
        for element in self.list.iter() {
            if element.name == p_name {
                return Some(element);
            }
        }
        None
    }

    fn add(&'t mut self, p_name: &'t str) -> Box<Element> {
        let new_element = Box::new(Element::new(p_name));
        self.list.push(*new_element);
        return new_element;
    }

    fn add_once(&'t mut self, p_name: &'t str) -> Box<Element> {
        match self.get(p_name) {
            Some(x) => Box::new(*x),
            None => self.add(p_name),
        }
    }
}

fn main() {
    let mut t = Top::new();
    let plop1 = t.add_once("plop1");
    let plop2 = t.add_once("plop1");
}

最佳答案

让我们先解决设计问题。主要问题是生命周期混淆:

struct Top<'t> {
    list: Vec<Element<'t>>,
}

impl<'t> Top<'t> {
    fn add(&'t mut self, p_name: &'t str) -> Box<Element>;
    fn add_once(&'t mut self, p_name: &'t str) -> Box<Element>;
}

您断言 self 应该至少与 't 一样长,这本身就是它将包含的引用的生命周期界限。这实际上不是您所需要的,self 应该比 t 活得,以保证任何 &tself 死之前,它还活着并且还在踢。

如果我们改变它,我们可以毫不费力地返回对 Element 的引用:

impl<'t> Top<'t> {
    fn add<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t>;
    fn add_once<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t>;
}

请注意,Element 引用的生命周期 'a't 的生命周期不同(实际上会更短)其包含的引用。

除此之外,这应该可以修复功能:

fn position(&self, p_name: &str) -> Option<usize> {
    self.list.iter().position(|e| e.name == p_name)
}

fn add<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t> {
    self.list.push(Element::new(p_name));
    &self.list[self.list.len() - 1]
}

fn add_once<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t> {
    if let Some(p) = self.position(p_name) {
        return &self.list[p];
    }
    self.add(p_name)
}

position 可以重用于get:

fn get<'a>(&'a self, p_name: &str) -> Option<&'a Element<'t>> {
    self.position(p_name).map(|pos| &self.list[pos])
}

我希望以下内容能够在完美的世界中工作:

fn add_once<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t> {
    match self.get(p_name) {
        Some(x) => x,
        None => self.add(p_name)
    }
}

但是,我记得有一次讨论认为借用检查器不够宽松:由 self.get 引起的借用范围被计算为整个 匹配 表达式,即使在 None 分支中无法访问临时文件。

一旦“非词法生命周期”被纳入 Rust,这应该得到修复,这是一项正在进行的工作。

关于Rust 在匹配表达式中借用可变的 self,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26908383/

相关文章:

rust - Rust 宏在特征定义中起作用吗?

rust - 为什么我不能从闭包返回引用?

python - 解析 docopt 参数类型(任何语言)

floating-point - 如何获取包含 float 的迭代器的最小值或最大值?

rust - 当用于分隔匹配表达式的分支时,大括号和逗号之间有什么区别?

rust - 为什么by_ref不能阻止.all使用迭代器?

arrays - 初始化固定长度数组的正确方法是什么?

error-handling - 无法将io::Error从可见结果中移出

rust - 返回一个 str - Rust Lifetimes

callback - 无法从回调向量调用函数,获取 `expected function, found ` Box<std::ops::FnMut(T) + 'a>`