我在 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
活得少,以保证任何 &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>;
}
请注意,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/