Here I have a working example. I take a new stdin
handle in main. I pass it to the function parse_something. In this function stdin.lock()
returns a lockguard which I store into a struct that is finally returned. Picture the struct being some sort of iterator. And I would like this iterator to keep stdin
locked as long as it is live. So far so good. The lockguard is linked to the lifetime 'a
. Which started at the new stdin
handle in main.
use std::io::BufRead;
struct SomeIterator<B: BufRead> {
buffer: B,
}
fn parse_something<'a>(stdin: &'a std::io::Stdin) -> SomeIterator<std::io::StdinLock<'a>> {
let r = SomeIterator {
buffer: stdin.lock(),
};
r
}
fn main() {
let stdin = std::io::stdin();
parse_something(&stdin);
}
Let's now try to move the stdin
handle instantiation inside the parse_something
function.
use std::io::BufRead;
struct SomeIterator<B: BufRead> {
buffer: B,
}
fn parse_something<'a>() -> SomeIterator<std::io::StdinLock<'a>> {
let stdin = std::io::stdin();
let r = SomeIterator {
buffer: stdin.lock(),
};
r
}
fn main() {
parse_something();
}
I get the error error[E0597]: `stdin` does not live long enough
. So far so good. stdin must live as long as 'a
. And for that, I should probably store the stdin handler inside SomeIterator
.
The catch is I also must take a .lock()
on the stdin
handle with the same lifetime 'a
. I cannot figure out how.
This doesn't work because the lockguard references the let stdin
variable, not on the (eventually moved) stdin
attribute within SomeIterator
.
struct SomeIterator<B: BufRead> {
stdin: std::io::Stdin,
buffer: B,
}
fn parse_something<'a>() -> SomeIterator<std::io::StdinLock<'a>> {
let stdin = std::io::stdin();
let guard = stdin.lock();
let r = SomeIterator {
stdin: stdin,
buffer: guard,
};
r
}
Eventually, I went all in, and added some random crap in the hope that perhaps I get lucky and stumble on the solution. No luck though. Here, I put stdin
in a box. Technically, the content of the box should never be moving around in memory. Only the box "pointer object" is. I would hope that r.stdin.lock()
actually uses the lifetime of the content within the Box. Which should be 'a
since the Box is part of SomeIterator
which itself is generic over 'a
.
Sadly everything goes to shit when r.stdin.lock()
actually borrows r.stdin
before calling .lock()
. This borrow only last for the scope of the function. `*r.stdin` does not live long enough
hits hard.
struct SomeIterator<'a, B: BufRead + 'a> {
stdin: Box<std::io::Stdin>,
buffer: Option<B>,
_marker: std::marker::PhantomData<&'a B>,
}
fn parse_something<'a>() -> SomeIterator<'a, std::io::StdinLock<'a>> {
let mut r = SomeIterator {
stdin: Box::new(std::io::stdin()),
buffer: None,
_marker: std::marker::PhantomData{},
};
r.buffer = Some(r.stdin.lock());
r
}
I suspect that after weeding out the details, my question ultimately becomes: is it possible in safe Rust to store a reference to a sibling attribute? But I could be wrong.