crhino
crhino

Reputation: 559

Unable to infer lifetime for borrow expression when using a trait with an explicit lifetime

use std::io::BufReader;
struct Foo {
    buf: [u8, ..10]
}

trait Bar<'a> {
    fn test(&self, arg: BufReader<'a>) {}
}

impl<'a, T: Bar<'a>> Foo {
    fn bar(&'a mut self, t: T) {
        t.test(BufReader::new(&self.buf));
        let b = &mut self.buf;
    }

    fn baz(&self, t: T) {
        t.test(BufReader::new(&self.buf));
    }
}

fn main() {}

The code above fails to compile, with the error message:

lifetimes.rs:17:31: 17:40 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
lifetimes.rs:17         t.test(BufReader::new(&self.buf));
                                              ^~~~~~~~~
lifetimes.rs:16:5: 18:6 help: consider using an explicit lifetime parameter as shown: fn baz(&'a self, t: T)
lifetimes.rs:16     fn baz(&self, t: T) {
lifetimes.rs:17         t.test(BufReader::new(&self.buf));
lifetimes.rs:18     }
error: aborting due to previous error

However, if I add the named lifetime parameter, I cannot mutable borrow the buf field after calling test, as seen in fn bar. Commenting out the fn baz and trying to compile results in:

lifetimes.rs:13:22: 13:30 error: cannot borrow `self.buf` as mutable because it is also borrowed as immutable
lifetimes.rs:13         let b = &mut self.buf;
                                     ^~~~~~~~
lifetimes.rs:12:32: 12:40 note: previous borrow of `self.buf` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `self.buf` until the borrow ends
lifetimes.rs:12         t.test(BufReader::new(&self.buf));
                                               ^~~~~~~~
lifetimes.rs:14:6: 14:6 note: previous borrow ends here
lifetimes.rs:11     fn bar(&'a mut self, t: T) {
lifetimes.rs:12         t.test(BufReader::new(&self.buf));
lifetimes.rs:13         let b = &mut self.buf;
lifetimes.rs:14     }
                    ^
error: aborting due to previous error

My understanding of this is that by adding the named lifetime 'a to the &'a mut self parameter, the reference taken by BufReader has a lifetime as long as the self reference is valid, which is until the end of the function. This conflicts with the mutable borrow of self.buf on the line after.

However, I am not sure why I need the named lifetime parameter on the self. It seems to me that the BufReader reference should be able to only exist for the lifetime of the t.test method call. Is the compiler complaining because the self.buf borrow must be ensured to live only as long as the &self borrow? How would I go about doing that while still only borrowing it for the lifetime of the method call?

Any help in going about fixing this problem and understanding more about the semantics here would be much appreciated!

Update

So I am still looking into this problem, and I have found this test case and this issue that show basically what I am trying to do. I would very much like to understand why the error pointed to by the test case link is an error.

I can see in the issue rustc output that attempts to point out what the error is, but I am having trouble understanding what exactly it is trying to say.

Upvotes: 0

Views: 3829

Answers (2)

Shepmaster
Shepmaster

Reputation: 430310

Edit

I'm going to copy-edit my comment here:

I originally thought that adding a lifetime or generic parameter to the trait / struct / enum was a shorthand for putting it on every method in the trait, but I was wrong. My current understanding is that you add a lifetime to the trait / struct / enum when that item needs to participate in the lifetime, likely because it is storing a reference with that lifetime.

struct Keeper<'a> {
    counts: Vec<&'a i32>,
}

impl<'a> Keeper<'a> {
    fn add_one(&mut self, count: &'a i32) {
        if *count > 5 {
            self.counts.push(count);
        }
    }

    fn add_two<'b>(&mut self, count: &'b i32) -> i32 {
        *count + 1
    }
}

fn main() {
    let mut cnt1 = 1;
    let mut cnt2 = 2;
    let mut k = Keeper { counts: Vec::new() };

    k.add_one(&cnt1);
    k.add_two(&cnt2);

    // cnt1 += 1; // Errors: cannot assign to `cnt1` because it is borrowed
    cnt2 += 1; // Just fine

    println!("{}, {}", cnt1, cnt2)
}

Here, we've added a lifetime to Keeper because it might store the reference it is given. The borrow checker must assume that the reference is stored for good when we call add_one, so once we call that method, we can no longer mutate the value.

add_two, on the other hand, creates a fresh lifetime that can only be applied to that function invocation, so the borrow checker knows that once the function returns, it is the One True Owner.

The upshot is, if you need to store a reference, then there's nothing you can do at this level. Rust can't make sure you are safe, and that's something it takes seriously.

However, I bet you don't need to store the reference. Move the <'a, T: Bar<'a>> from the impl to the fn and you'll be good to go.

Said another way: I bet you should never have impl<A> if your trait or struct don't require it. Put the generics on the methods instead.

Original

This compiles, but I'm not 100% sure it does what you intended:

impl Foo {
    fn baz<'a, T: Bar<'a>>(&'a self, t: T) {
        t.test(BufReader::new(&self.buf));
    }
}

I fell into this trap myself, so I'll paste what I was told:

Everything in the impl block is parameterized. I've actually never seen type parameters added to impl blocks themselves that aren't part of the trait or type definition. It's far more common to parameterize the individual methods that need it.

Perhaps other comments / answers can help explain in further detail.

Upvotes: 1

kbknapp
kbknapp

Reputation: 302

Removing all explicit lifetimes also works. I've found that I only add lifetimes when I'm sure I need them (i.e. to specifiy that two lifetimes should intersect at a given point which can't be known to the compiler).

I'm not sure exactly what you're going for, but this compiles (on rustc 0.13.0-nightly (cc19e3380 2014-12-20 20:00:36 +0000)).

use std::io::BufReader;
struct Foo {
    buf: [u8, ..10]
}

trait Bar {
    fn test(&self, arg: BufReader) {}
}

impl<T: Bar> Foo {
    fn bar(&mut self, t: T) {
        t.test(BufReader::new(&self.buf));
        let b = &mut self.buf;
    }

    fn baz(&self, t: T) {
        t.test(BufReader::new(&self.buf));
    }
}

Upvotes: 2

Related Questions