Hello,
I'm currently trying to implement a weird data structure : a stack of path components (that can be popped and pushed from/at the end). What's special is that instead of storing the components in a vector-like structure, I'd like to use the call stack. Basically, I'd have a backing String
somewhere on the call stack, and each call frame on top of it would have a reference to it (inside a struct) with the number of characters of the current path component of the stack frame, so the String
can be truncated of its last component when the struct is dropped.
Just for reference, these are not file system paths, so I can use String
s without feeling bad about compatibility.
pub struct Path<'a> {
string: &'a mut String,
last_component_size: usize,
}
impl<'a> Path<'a> {
pub fn new(backing: &'a mut String) -> Self {
Self {
string: backing,
last_component_size: 0,
}
}
pub fn join<'b: 'a, 'c>(&'b mut self, component: &'c str) -> Path<'b> {
const SEP: char = '/';
self.string.push(SEP);
self.string.push_str(component);
Self {
string: self.string,
last_component_size: SEP.len_utf8() + component.len(),
}
}
}
impl<'a> Drop for Path<'a> {
fn drop(&mut self) {
let new_len = self.string.len() - self.last_component_size;
self.string.truncate(new_len);
}
}
However, I cannot use this piece of code as I would like to. For instance, this doesn't compile:
#[test]
fn test_call_rec() {
let mut backing = String::with_capacity(4);
rec(Path::new(&mut backing), 0);
}
fn rec(mut path: Path, n: u8) {
match n {
0 => {
rec(path.join("1"), 1);
rec(path.join("2"), 2);
}
1 => {
rec(path.join("2"), 2);
rec(path.join("2"), 2);
rec(path.join("2"), 2);
}
_ => {
dbg!(path.string);
}
}
}
for these reasons (only one error shown):
error[E0597]: `path` does not live long enough
--> back/src/form_parser/path.rs:56:21
|
53 | fn rec(mut path: Path, n: u8) {
| -------- has type `path::Path<'1>`
...
56 | rec(path.join("1"), 1);
| ^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `path` is borrowed for `'1`
...
68 | }
| - `path` dropped here while still borrowed
I would have guessed that Path::join
would create a new path for the duration of the function call (capturing exclusive mutability to the self
path) and drop it just after the function call. But it doesn't seem to be the case. Why is path still borrowed ?
I have quite a bit of experience in Rust but I'm really lost here. I have no idea of which safety guarantees I'm unknowingly trying to break.