我在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");
}
答案 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,这应该是固定的,这是一项正在进行中的工作。