2017-01-09 8 views
0

私は一連の質問を連続して聞くことができるPromptSetを構築しています。テスト上の理由から、stdin & stdoutを直接使用する代わりに、リーダーとライターを渡すことができます。コンストラクタの標準入力と出力を使用して、構築する構造体が存続する限り存続する方法はありますか?

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オブジェクトを引数として取ることです。

あなたはそれを回避することができます。はい、私は知っている、私はちょうど正反対だと言ったが、それは "私以外の誰もがスタン/ stdoutを使用することはありません" (別名、println!はもう動作しません!)leakクレートを使用してStdin&'static StdinにリークさせるとStdinLock<'static>となります。

+0

ええ、私は静的な生涯を考えましたが、うわー、それも問題を引き起こすとは思いませんでした。私は使いやすさの問題を長期間にわたり 'stdin'と' stdout'を支配する上で見ることができるので、おそらく私はこれを許すでしょう。ご説明ありがとうございます。 – webdesserts

+0

HelperInとHelperInは 'Stdin'と' Stdout'のインスタンスを含むクラスで、 'PrompSet 'を返す 'default'を実装することはできません。 'BufRead'と' Write'をそれぞれ実行します(各操作のストリームをロックし、ロックガードで適切なメソッドを呼び出す)。それは可能であるはずですが、私がそれを書いたとき、 'BufRead :: fill_buf'が生涯の要件を満たさないためにコンパイルされませんでした。 – user4815162342

+0

これは、壊れた 'BufRead'実装を作成します。あなたは 'BufRead'呼び出しの中で読み込まれなかったすべてのバッファされたバイトを失います。できることは自分でバッファリングを実装することですが、別のリーダー間で任意のバイトを混在させることになります。 'StdinLock'はこれほどうまく動かないという理由があります。 –

0

あなたの質問に対する回答ではなく、同様の問題になるかもしれません。ここに私の解決策があります。

ここでは、1行ごとに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); 
    } 
} 
関連する問題