Here is how you can get rid of the spurious errors. I am new to Rust so there may be serious errors in the following explanation.
use std::str::Chars;
struct A<'a> {
chars: Chars<'a>,
}
The 'a
here is a lifetime parameter (just like template parameters in C++). Types can be parameterised by lifetimes in Rust.
The Chars
type also takes a lifetime parameter. What this implies is that the Chars
type probably has a member element which needs a lifetime parameter. Lifetime parameters only make sense on references (since lifetime here actually means "lifetime of a borrow").
We know that Chars
needs to keep a reference to the string from which it was created, 'a
will probably be used to denote the source string's lifetime.
Here we simply supply 'a
as the lifetime parameter to Chars
telling the Rust compiler that the lifetime of Chars
is the same as the lifetime of the struct A
. IMO "lifetime 'a of type A" should be read as "lifetime 'a of the references contained in the struct A".
I think the struct implementation can be parameterised independently from the struct itself hence we need to repeat the parameters with the impl
keyword. Here we bind the name 'a to the lifetime of the struct A.
impl<'a> A<'a> {
The name 'b
is introduced in the context of the function f2
. Here it is used to bind with the lifetime of the reference &mut self
.
fn f2<'b>(&'b mut self) {}
The name 'b
is introduced in the context of the function f1
.This 'b
does not have a direct relationship with the 'b
introduced by f2
above.
Here it is used to bind with the lifetime of the reference &mut self
. Needless to say this reference also does not have any relationship with the &mut self
in the previous function, this is a new independent borrow of self
.
Had we not used explicit lifetime annotation here Rust would have used its lifetime elision rules to arrive at the following function signature...
//fn f1<'a>(&'a mut self) -> Option<Chars<'a>>
As you can see this binds the lifetime of the reference &mut self
parameter to the lifetime of the Chars
object being returned from this function (this Chars
object need not be the same as self.chars
) this is absurd since the returned Chars
will outlive the &mut self
reference. Hence we need to separate the two lifetimes as follows...
fn f1<'b>(&'b mut self) -> Option<Chars<'a>> {
self.chars.next();
Remember &mut self
is a borrow of self
and anything referred to by &mut self
is also a borrow. Hence we cannot return Some(self.chars)
here. self.chars
is not ours to give (Error: cannot move out of borrowed content.).
We need to create a clone of self.chars
so that it can be given out.
Some(self.chars.clone())
Note here the returned Chars
has the same lifetime as the struct A.
And now here is f3
unchanged and without compilation errors!
fn f3<'b>(&'b mut self) {
if let Some(x) = self.f1() { //This is ok now
} else {
self.f2() //This is also ok now
}
}
The main function just for completeness...
fn main() {
let mut a = A { chars:"abc".chars() };
a.f3();
for c in a.chars {
print!("{}", c);
}
}
I have updated the code the make the lifetime relationships clearer.