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

后端 未结 2 1532
没有蜡笔的小新
没有蜡笔的小新 2021-01-21 12:36

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

相关标签:
2条回答
  • 2021-01-21 13:18

    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(),
        }
    }
    
    0 讨论(0)
  • 2021-01-21 13:19

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题