I have a piece of code that needs to store String
s and access references to those strings. I first wrote it as follows:
struct Pool {
strings : Vec<String>
}
impl Pool {
pub fn new() -> Self {
Self {
strings: vec![]
}
}
pub fn some_f(&mut self) -> Vec<&str> {
let mut v = vec![];
for i in 1..10 {
let string = format!("{}", i);
let string_ref = self.new_string(string);
v.push(string_ref);
}
v
}
fn new_string(&mut self, string : String) -> &str {
self.strings.push(string);
&self.strings.last().unwrap()[..]
}
}
This does not pass the borrow checker:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/main.rs:19:30
|
14 | pub fn some_f(&mut self) -> Vec<&str> {
| - let's call the lifetime of this reference `'1`
...
19 | let string_ref = self.new_string(string);
| ^^^^ mutable borrow starts here in previous iteration of loop
...
23 | v
| - returning this value requires that `*self` is borrowed for `'1`
So apparently the borrow-checker isn't smart enough to realize that the mutable borrow doesn't extend beyond the call to new_string
. I tried separating the part that mutates the structure from retrieving references, arriving at this code:
use std::vec::*;
struct Pool {
strings : Vec<String>
}
impl Pool {
pub fn new() -> Self {
Self {
strings: vec![]
}
}
pub fn some_f(&mut self) -> Vec<&str> {
let mut v = vec![];
for i in 1..10 {
let string = format!("{}", i);
self.new_string(string);
}
for i in 1..10 {
let string = &self.strings[i - 1];
v.push(&string[..]);
}
v
}
fn new_string(&mut self, string : String) {
self.strings.push(string);
}
}
This is semantically equivalent (hopefully) and does compile. However doing as much as combining the two for
loops into one:
for i in 1..10 {
let string = format!("{}", i);
self.new_string(string);
let string = &self.strings[i - 1];
v.push(&string[..]);
}
gives a similar borrowing error:
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:19:13
|
14 | pub fn some_f(&mut self) -> Vec<&str> {
| - let's call the lifetime of this reference `'1`
...
19 | self.new_string(string);
| ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
20 | let string = &self.strings[i - 1];
| ------------ immutable borrow occurs here
...
24 | v
| - returning this value requires that `self.strings` is borrowed for `'1`
I have several questions:
Why is the borrow checker so strict as to extend the mutable borrow for the entire duration of the loop in this case? Is it impossible/very hard to analyse that the
&mut
passed tonew_string
does not leak beyond that function call?Is it possible to fix this issue with custom lifetimes so that I can go back to my original helper that both mutates and returns a reference?
Is there a different, more Rust-idiomatic way that doesn't upset the borrow checker in which I could achieve what I want, i.e. have a structure that mutates and returns references to itself?
I found this question, but I don't understand the answer (is it a negative answer to #2? no idea) and most other questions have issues with explicit lifetime parameters. My code uses only inferred lifetimes.