Is there a way to use locked standard input and output in a constructor to live as long as the struct you're constructing?

余生长醉 提交于 2019-12-02 05:41:45
oli_obk

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(),
    }
}
Dzmitry Lazerka

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);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!