2017-01-09 14 views
0

我正在構建一個可以連續詢問一系列問題的PromptSet。出於測試原因,它允許您直接傳遞讀寫器,而不是直接使用stdin &標準輸出。有沒有一種方法可以在構造函數中使用標準輸入和輸出,只要你正在構造的結構體一直存在?

由於stdin和stdout是常見用例,我想創建一個默認的「構造函數」,允許用戶在不需要任何參數的情況下生成PromptSet<StdinLock, StdoutLock>。這裏是到目前爲止的代碼:

use std::io::{self, BufRead, StdinLock, StdoutLock, Write}; 

pub struct PromptSet<R, W> 
where 
    R: BufRead, 
    W: Write, 
{ 
    pub reader: R, 
    pub writer: W, 
} 

impl<R, W> PromptSet<R, W> 
where 
    R: BufRead, 
    W: Write, 
{ 
    pub fn new(reader: R, writer: W) -> PromptSet<R, W> { 
     return PromptSet { 
      reader: reader, 
      writer: writer, 
     }; 
    } 

    pub fn default<'a>() -> PromptSet<StdinLock<'a>, StdoutLock<'a>> { 
     let stdin = io::stdin(); 
     let stdout = io::stdout(); 

     return PromptSet { 
      reader: stdin.lock(), 
      writer: stdout.lock(), 
     }; 
    } 

    pub fn prompt(&mut self, question: &str) -> String { 
     let mut input = String::new(); 

     write!(self.writer, "{}: ", question).unwrap(); 
     self.writer.flush().unwrap(); 
     self.reader.read_line(&mut input).unwrap(); 

     return input.trim().to_string(); 
    } 
} 

fn main() {} 

StdinLockStdoutLock都需要聲明的壽命。爲了使它複雜化,我認爲最初的stdin()/stdout()句柄需要至少與鎖一樣長。我希望參考StdinLockStdoutLock只要我的PromptSet能夠生活,但無論我嘗試什麼,我都無法使其工作。這裏是我不斷收到錯誤:

error[E0597]: `stdin` does not live long enough 
    --> src/main.rs:30:21 
    | 
30 |    reader: stdin.lock(), 
    |      ^^^^^ borrowed value does not live long enough 
... 
33 |  } 
    |  - borrowed value only lives until here 
    | 
note: borrowed value must be valid for the lifetime 'a as defined on the method body at 25:5... 
    --> src/main.rs:25:5 
    | 
25 |/ pub fn default<'a>() -> PromptSet<StdinLock<'a>, StdoutLock<'a>> { 
26 | |   let stdin = io::stdin(); 
27 | |   let stdout = io::stdout(); 
28 | | 
... | 
32 | |   }; 
33 | |  } 
    | |_____^ 

error[E0597]: `stdout` does not live long enough 
    --> src/main.rs:31:21 
    | 
31 |    writer: stdout.lock(), 
    |      ^^^^^^ borrowed value does not live long enough 
32 |   }; 
33 |  } 
    |  - borrowed value only lives until here 
    | 
note: borrowed value must be valid for the lifetime 'a as defined on the method body at 25:5... 
    --> src/main.rs:25:5 
    | 
25 |/ pub fn default<'a>() -> PromptSet<StdinLock<'a>, StdoutLock<'a>> { 
26 | |   let stdin = io::stdin(); 
27 | |   let stdout = io::stdout(); 
28 | | 
... | 
32 | |   }; 
33 | |  } 
    | |_____^ 

這是完全有可能我就是不明白,壽命或別的超級基本的概念。

+0

[有沒有辦法恢復到一個函數創建一個變量的引用?](http://stackoverflow.com/q/32682876/155423) – Shepmaster

+0

如果問題被改寫到標準輸入/輸出它的不是重複的,因爲stdin/stdout是一個相當特殊的情況。 –

回答

3

lock方法的簽名是fn lock(&self) -> StdinLock,當使用生命週期註釋完全展開時,其爲fn lock<'a>(&'a self) -> StdinLock<'a>。因此StdinLock只能在lock方法被調用時才能存活。由於您在此功能中定義了stdin,因此StdinLock無法運行。這與返回對本地值的引用相同。

你不能這樣做,你不能解決它。唯一的修復方法是使default方法以StdinStdout對象作爲參數。

這就是說,你可以解決它。是的,我知道,我只是說了完全相反的事情,但它更像是一個「沒有人比我會使用標準輸入/標準輸出」(又名,println!將不再工作!)。您可以使用leak箱子將Stdin泄漏到&'static Stdin,這將產生StdinLock<'static>

+0

是的,我已經考慮過「靜態」的一生,但是,哇,我不認爲這也會導致問題。我可以看到長時間控制'stdin'&'stdout'的可用性問題,所以也許我會放過它。謝謝你的解釋。 – webdesserts

+0

當'HelperIn'和'HelperOut'是包含(自己)Stdin和Stdout實例的類時,不可能實現返回'PrompSet '的'default',並實現'BufRead'和'Write'分別(通過鎖定每個操作的流並在lock guard上調用適當的方法)?它似乎應該是可能的,但是當我編寫它時,由於'BufRead :: fill_buf'不能滿足生命期要求,它不會編譯。 – user4815162342

+0

這會創建一個破壞的'BufRead'實現。您將丟失BufRead調用中未讀取的所有緩衝字節。你可以做的是自己實現緩衝,但是你會在不同的閱讀器之間混合任意字節。有一個原因,「StdinLock」不能像這樣工作。 –

0

可能不是你的問題的答案,而是一個類似的問題。這是我的解決方案。

這裏的主要技巧是撥打stdin.lock()每一行。

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); 
    } 
} 
相關問題