Rust在比赛表达中借用可变的自我

时间:2014-11-13 11:57:33

标签: rust

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

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返回一个值然后调用该函数来解决它。但是,这不起作用,因为条件的含义不是选择值而是选择性地执行操作。

完整的代码示例如下:

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");
}

1 个答案:

答案 0 :(得分:3)

让我们先解决设计问题。主要问题是终身合并:

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,以确保任何&'t仍然存在并且在self死亡之前一直踢

如果我们更改此内容,我们可以轻松地返回对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>;
}

请注意,对'a的引用的生命周期Element与其包含的引用的生命周期'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引起的借用范围被计算为整个match表达式,即使在None分支中,临时无法访问。

一旦“非词汇生命期”被纳入Rust,这应该是固定的,这是一项正在进行中的工作。