Rust borrows a mutable internal match expression
I have a self
borrowing problem in an expression 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),
}
}
Function signature get()
and add()
:
fn get(&self, p_name: &str) -> Option<&Element>
fn add(&'t mut self, p_name: &'t str) -> Box<Element>
The compiler discards this code:
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
I get this, but I can't see how I can rewrite the expression match
.
In a related question , it was solved by returning a match
value and then calling a function. However, this does not work here, because the meaning of the conditional value is not the selection of the value, but the selective execution of the action.
Below is the complete sample code:
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");
}
source to share
Fix the design issues first. The main problem is the unification of life:
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>;
}
You submit that you self
must live at least as long as 't
, which in itself is the limit of the duration of the links it will contain. This is not really what you want, you have self
to live less 't
to ensure that anyone &'t
is still alive and kicking until self
they die.
If we change this, we will painlessly bring back links to 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>;
}
Note that the lifetime of a 'a
link is not Element
different (and will actually be shorter) than the lifetime of the 't
link it contains.
To that end, this should fix the functions:
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)
}
And position
can be reused for get
:
fn get<'a>(&'a self, p_name: &str) -> Option<&'a Element<'t>> {
self.position(p_name).map(|pos| &self.list[pos])
}
I expect the following to work in a perfect world:
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)
}
}
However, I recall a discussion in which it was determined that the borrowing mechanism is not weaker enough: the amount of borrowing caused self.get
is evaluated as a full expression match
, although the None
temporary access branch cannot be accessed.
This should be fixed once "Non-Lexical Lifetimes" were enabled in Rust, which are in development.
source to share