Reputation: 46685
Consider the following code:
struct Foo<'a> {
borrowed: &'a u8,
owned_one: Vec<u8>,
owned_two: Vec<u8>,
output: usize
}
impl<'a> Foo<'a> {
fn do_stuff(&mut self) {
self.output = self.owned_one.len();
let zipped = self.owned_one.iter().zip(self.owned_two.iter());
Self::subroutine(&zipped);
}
fn subroutine<Arg: Iterator<Item=(&'a u8, &'a u8)>>(_zipped: &Arg) {}
}
fn main() {
let num = 0u8;
let mut foo = Foo {
borrowed: &num,
owned_one: vec![0],
owned_two: vec![1],
output: 0
};
foo.do_stuff();
let _out = &foo.output;
}
It doesn't compile, producing the following error:
error: lifetime may not live long enough
--> src/lib.rs:12:9
|
8 | impl<'a> Foo<'a> {
| -- lifetime `'a` defined here
9 | fn do_stuff(&mut self) {
| - let's call the lifetime of this reference `'1`
...
12 | Self::subroutine(&zipped);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`
I don't fully understand the complaint - surely self
would always have the lifetime assigned to the class we're impl
ementing? - but I can understand that both arguments to zip()
need to last the same time. So I change do_stuff
to take &'a mut self
.
struct Foo<'a> {
borrowed: &'a u8,
owned_one: Vec<u8>,
owned_two: Vec<u8>,
output: usize
}
impl<'a> Foo<'a> {
fn do_stuff(&'a mut self) {
self.output = self.owned_one.len();
let zipped = self.owned_one.iter().zip(self.owned_two.iter());
Self::subroutine(&zipped);
}
fn subroutine<Arg: Iterator<Item=(&'a u8, &'a u8)>>(_zipped: &Arg) {}
}
fn main() {
let num = 0u8;
let mut foo = Foo {
borrowed: &num,
owned_one: vec![0],
owned_two: vec![1],
output: 0
};
foo.do_stuff();
let _out = &foo.output;
}
However, now compilation fails with:
error[E0502]: cannot borrow `foo.output` as immutable because it is also borrowed as mutable
--> src/lib.rs:27:16
|
26 | foo.do_stuff();
| -------------- mutable borrow occurs here
27 | let _out = &foo.output;
| ^^^^^^^^^^^
| |
| immutable borrow occurs here
| mutable borrow later used here
Why has specifying a lifetime for self
in the argument list for do_stuff
meant that I suddenly can't take the immutable reference to foo
later; and what can I do about it?
Upvotes: 2
Views: 53
Reputation: 26765
writing fn do_stuff(&'a mut self)
in this context mean that the lifetime of this borrow of self, must life as long as what this self has borrow borrowed
. That very often not what you want.
Your mistake is here fn subroutine<Arg: Iterator<Item=(&'a u8, &'a u8)>>(_zipped: &Arg) {}
. Think of lexical meaning of your declaration. Your method return nothing but require a "parent" lifetime ? There is no reason to. The simple solution is simply to introduce a new lifetime for your Item. Like fn subroutine<'i, Arg: Iterator<Item=(&'i u8, &'i u8)>>(_zipped: &Arg) {}
. Unless your function return something link to 'a
, no lifetime of parent should be here.
Also, it's better to accept IntoIterator
it's more generic, and there is no reason to take it by reference and finally when you have such complex generic better use where
, and really really if you want to be pedantic you need two lifetime:
fn subroutine<'i, 'j, Arg>(_zipped: Arg)
where
Arg: IntoIterator<Item = (&'i u8, &'j u8)>,
{
}
Upvotes: 3