Newbie lifetime issue (involving Box<Read>, stdin.lock())


#1

I’m writing a parser. My main parse function takes a &mut Box<Read>. I can get this to work with stdin, but can’t get it to work with stdin.lock():

use std::io::{self, Read};

fn main() {
    let stdin = io::stdin();
    let mut handle = stdin.lock();
    let mut read_box: Box<Read> = Box::new(handle);
    read(&mut read_box);
}

fn parse(source: &mut Box<Read>) {
    ...
}

The error:

bb.rs:5:22: 5:27 error: `stdin` does not live long enough
bb.rs:5     let mut handle = stdin.lock();
                             ^~~~~
note: reference must be valid for the static lifetime...
bb.rs:4:29: 8:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 4:28
...
...

Any idea what I’m doing wrong?

Tangent: I’m using Box<Read> instead of a T:Read type parameter because I don’t want to the parser code to be specialized for each type of input source (stdin, file, etc). Does that make sense or am I misunderstanding things?


#2

The problem is that Box<Trait> is actually Box<Trait + 'static>, meaning that whatever type is hiding behind Trait has to not be bound to any lifetime shorter than 'static.

This is a problem, because .lock() is borrowing stdin, and stdin (by virtue of being a local) is not 'static.

The solution is to be less restrictive:

fn parse<'a>(source: &mut Box<Read + 'a>) {
    ...
}

This allows the type behind Read to be borrowed for just the duration of the call to parse.


#3

Ah, thanks!

It looks like lifetime elision does this automatically for &T (which is what allowed me to be ignorant of this until now) but not for Box<T>. Is that correct?


#4

No; that would imply that &T is bound by the 'static lifetime, which isn’t the case.

In the case of &T, the trait implicitly has the same lifetime as the pointer itself; &Trait is really &'a (Trait + 'a). Boxes don’t have lifetime restrictions, so it makes sense that they get 'static by default.