Reputation: 574
Consider the following Program:
fn func(source: &str, state: & Vec<&str>) {
println!("{}", source);
println!("{}", state[0]);
}
fn step<'a>(source: &'a str, state: &mut Vec<&'a str>) {
state.push(&source[4..10]);
func(source, state);
state.push(&source[4..10]);
}
fn main() {
let source = "abcdefghijklmnopqrstuvwxyz";
{
let mut state = Vec::<&str>::new();
step(source, &mut state);
step(source, &mut state);
step(source, &mut state);
}
}
This compiles and runs without errors. I can totally understand why the lifetime specifier 'a is needed here, because otherwise you could run:
fn main() {
let mut state = Vec::<&str>::new();
{
let source = "abcdefghijklmnopqrstuvwxyz";
step(source, &mut state);
step(source, &mut state);
step(source, &mut state);
}
println!("{}", state[0]);
}
which would result in undefined output. My problem is that I want to pack both arguments of "func" into a structure, but I can't make this work:
struct MyStruct<'a, 'b> {
pub source: &'a str,
pub state: &'b mut Vec<&'b str>,
}
fn func(arg: MyStruct) {
println!("{}", arg.source);
println!("{}", arg.state[0]);
}
fn step<'a>(source: &'a str,
state: &mut Vec<&'a str>) {
state.push(&source[4..10]);
let s = MyStruct{source: source, state: state};
func(s);
state.push(&source[4..10]);
}
fn main() {
let source = "abcdefghijklmnopqrstuvwxyz";
{
let mut state = Vec::<&str>::new();
step(source, &mut state);
step(source, &mut state);
step(source, &mut state);
}
}
The code above does not compile with the error message:
107 | state: &mut Vec<&'a str>) {
| ----- consider changing the type of `state` to `&'a mut std::vec::Vec<&'a str>`
108 | state.push(&source[4..10]);
109 | let s = MyStruct{source: source, state: state};
| ^^^^^ lifetime `'a` required
If I change the state of state to &'a mut Vec<&'a str>, the build failes with the following message:
117 | step(source, &mut state);
| ----- first mutable borrow occurs here
118 | step(source, &mut state);
| ^^^^^ second mutable borrow occurs here
119 | step(source, &mut state);
120 | }
| - first borrow ends here
I would be thankful if anyone could tell me how to make this work.
Note: In this example program, "source" is a String with static lifetime. In the "real" program "source" does not have a static lifetime, but the content of a file that the program reads during runtime. "source" lives as long or longer than "state"
Upvotes: 3
Views: 190
Reputation: 13792
The lifetime situation in your function parameters is this:
fn step<'a>(source: &'a str,
state: &mut Vec<&'a str>)
Let's name the reference that doesn't have an explicit name on the lifetime:
fn step<'a, 'x>(source: &'a str,
state: &'x mut Vec<&'a str>)
There, now all the references have explicit lifetimes and we can see that the configuration in the struct does not correspond to how they relate to each other in the function parameters.
If your struct should follow the same relations we want this:
struct MyStruct<'a: 'x, 'x> {
pub source: &'a str,
pub state: &'x mut Vec<&'a str>,
}
And since this is a type definition, we need to add that relation that was implicit in the function that 'a: 'x
“'a outlives 'x”
Keep in mind you also need to scope the borrow that the s
variable of type MyStruct holds -- it borrows the state mutably (mutably means exclusively). So you need to insert scopes around the line with let s = ..
and f(s)
so that s
's scope ends before the next step call:
{
let s = MyStruct{source: source, state: state};
func(s);
}
Upvotes: 2