I\'m building a PromptSet
that can ask a series of questions in a row. For testing reasons, it allows you to pass a reader and writer instead of using stdin & s
The lock
method's signature is fn lock(&self) -> StdinLock
, which, when fully expanded with lifetime annotations, is fn lock<'a>(&'a self) -> StdinLock<'a>
. Thus the StdinLock
can only live as long as the value that the lock
method is called on. Since you defined stdin
in this very function, the StdinLock
can't outlive the function. This is the same as returning a reference to a local value. You also can't return the reference and the referred-to value together.
You can't do this, and you can't work around it. The only fix is to have the default
method take a Stdin
and a Stdout
object as arguments.
That said, you can work around it. Yes I know, I just said the exact opposite, but it's more of a "no one other than me will ever use stdin/stdout" (a.k.a., println!
will not work anymore!).
In Rust 1.26, you can use Box::leak to leak the Stdin
to a &'static Stdin
, which will yield a StdinLock<'static>
. Before Rust 1.26, you can use the leak crate:
pub fn default() -> PromptSet<StdinLock<'static>, StdoutLock<'static>> {
let stdin = Box::leak(Box::new(io::stdin()));
let stdout = Box::leak(Box::new(io::stdout()));
PromptSet {
reader: stdin.lock(),
writer: stdout.lock(),
}
}
Might be not really the answer to your question, but to a similar problem. Here's my solution.
The main trick here is to call stdin.lock()
for every single line.
use std::io;
use std::io::prelude::*;
use std::io::Stdin;
struct StdinWrapper {
stdin: Stdin,
}
impl Iterator for StdinWrapper {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
let stdin = &self.stdin;
let mut lines = stdin.lock().lines();
match lines.next() {
Some(result) => Some(result.expect("Cannot read line")),
None => None,
}
}
}
/**
* Callers of this method should not know concrete source of the strings.
* It could be Stdin, a file, DB, or even aliens from SETI.
*/
fn read() -> Box<Iterator<Item = String>> {
let stdin = io::stdin();
Box::new(StdinWrapper { stdin })
}
fn main() {
let lines = read();
for line in lines {
println!("{}", line);
}
}