Lifetime: do not understand error message

Yes, lifetime question again :slight_smile:
Can somebody translate comiler into english please.

First, there is no reference to E0495: Rust Compiler Error Index
Second, I have no idea what compiler is trying to tell me "the lifetime cannot outlive the anonymous lifetime #1"? Compiler shows "anonymous lifetime #1", but what is the first one???
Puzzled.

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
   --> src/lib.rs:205:21
    |
205 |             let s = self;
    |                     ^^^^
    |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 202:5...
   --> src/lib.rs:202:5
    |
202 | /     fn next(&mut self) -> Option<i64> {
203 | |
204 | |         if self.data_offset >= self.buffer.len() {
205 | |             let s = self;
...   |
215 | |         Some(res)
216 | |     }
    | |_____^
note: ...so that expression is assignable (expected &mut ColumnIter<'a>, found &mut ColumnIter<'a>)
   --> src/lib.rs:205:21
    |
205 |             let s = self;
    |                     ^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 198:1...
   --> src/lib.rs:198:1
    |
198 | / impl<'a> Iterator for ColumnIter<'a> {
199 | |     // TODO: what's the right  way to act if error in format in iterator?
200 | |
201 | |     type Item = i64;
...   |
216 | |     }
217 | | }
    | |_^
note: ...so that types are compatible (expected &mut ColumnIter<'_>, found &mut ColumnIter<'a>)
   --> src/lib.rs:206:31
    |
206 |             let next_page = s.next_page();
    |                               ^^^^^^^^^

https://github.com/vchekan/rust-parquet/blob/b64e646151d6421cd35f83c16567ce149bc2d2c5/src/lib.rs#L205

This is type inference at “its best” :slight_smile:

s.next_page() wants a &'a mut self but you’ve got a essentially &'elided mut self.

Why does next_page need 'a scoped mut borrow of self?

Ok, I have something like file handler. It contains file metadata and buffer. When I read the whole page, I need to read the next one. Reading page modifies File position and buffer, so it mutates FileInfo.
But if I remove scope from next_page as this: "fn next_page(&mut self) -> Result<()>" then I get:

error[E0596]: cannot borrow field `self.reader.protocol` of immutable binding as mutable
   --> src/lib.rs:138:9
    |
99  |     reader: &'a FileInfo,
    |             ------------ use `&'a mut FileInfo` here to make mutable
...
138 |         self.reader.protocol.inner().seek(SeekFrom::Start(self.next_page_offset)).expect("Failed to seek to column metadata");
    |         ^^^^^^^^^^^^^^^^^^^^ cannot mutably borrow field of immutable binding

So I am just following compiler advise.

That part is fine and correct but you shouldn’t need to borrow self mutably for 'a as well (compiler was telling you about needing a 'a mut borrow for reader, not self). Try dropping the 'a from next_page.

Hmm, I think I fixed the last error in wrong way, which caused the next error. What the last one is complaining about is that self.reader is immutable. And indeed, it is:

pub struct ColumnIter<'a> {
    reader: &'a FileInfo,

where it should be

pub struct ColumnIter<'a> {
    reader: &'a **mut** FileInfo,

Now this fragment passes. Let me work on remaining errors...

Right, and you should change next_page to fn next_page(&mut self) (ie drop the 'a from self borrow) if you haven’t yet.

It works! Thanks!

Seems you noticed immediately that "scoped mut borrow of self" is suspicious. Are there any considerations that help you to spot it?

A &'a mut self where 'a is a lifetime parameter on the type itself is almost always wrong. Even if the code compiles, you’ve likely set yourself up for compile failures when using this method. The reason is because this ends up borrowing self mutably for its entire life, and you’ll be very likely unable to use it from that point forward. You want borrows to be as short as possible, which the compiler will do its best to achieve - unless you force its hand otherwise :slight_smile:

Oh, and a dead giveaway that you definitely didn’t need such scope is because these methods weren’t returning any references at all. If you were, eg, returning the &'a mut FileInfo from there, then you’d need a &'a mut self to go along with it. But that would be a design smell in its own right.

4 Likes

Also, another thing that you may find helpful is to think like the compiler (or more precisely, the borrow checker). What’s it trying to prove? That you don’t violate the borrowing rules. Those, in a nutshell, is that you can have multiple immutable (aka shared) borrows XOR a single mutable (aka unique) borrow.

Lifetimes are a way the type system allows you to communicate with the borrow checker and to indicate your intent. It’ll enforce them for you and also will ensure that your lifetime requirements are coherent (this is where you may have seen the “cannot infer an appropriate lifetime for ...” errors - it’s saying the lifetimes don’t make sense). So when you specify a lifetime somewhere explicitly, try to understand what it’s indicating to the borrow checker. And then, if it complains, try to understand why - is there a violation of the rules that can occur if it did allow the code through? Is there potential for a dangling reference? That type of thing.

4 Likes

Just an unrelated drive-by side comment. A more rustic way to write this would be:

    if self.data_offset >= self.buffer.len() {
        if let Err(_) = self.next_page() {
            return None
        }
    }

If adventurous, you can also use the unstable Try trait in nightly to make it briefer:

    if self.data_offset >= self.buffer.len() {
        self.next_page().ok()?
    }

But that might be too dense for those unfamiliar.

Thanks @marcianx, I've incorporated let Err() syntax.

No need for nightly here; ?-on-Option was stabilized about 3 months ago in Rust 1.22: Announcing Rust 1.22 (and 1.22.1) | Rust Blog

1 Like

GTK! I suppose only the Try trait itself is not stabilized.