2017-01-09 4 views
0

일련의 질문을 연속적으로 수행 할 수있는 PromptSet을 구축 중입니다. 테스트 목적으로 stdout & 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>입니다. 따라서 StdinLocklock 메서드가 호출 된 값만큼 지속될 수 있습니다. 바로이 함수에 stdin을 정의 했으므로 StdinLock은 함수를 오래 사용할 수 없습니다. 이는 로컬 값에 대한 참조를 반환하는 것과 같습니다.

이렇게 할 수 없으므로 해결할 수 없습니다. 유일한 해결책은 default 메서드가 StdinStdout 개체를 인수로 사용하는 것입니다.

즉, 문제를 해결할 수 있습니다. 예, 저는 방금 정반대라고 말했지만, "제게 다른 사람은 아무도 없습니다."(별칭 : println!) 더 이상 작동하지 않습니다.. leak 크레이트를 사용하여 Stdin&'static Stdin으로 유출하면 StdinLock<'static>이됩니다.

+0

그래, 나는 '정적'평생을 고려해 봤지만 와우, 나는 그것이 문제를 일으킬 것이라고 생각하지 않았다. 오랜 시간 동안'stdin'과'stdout'을 제어 할 때 유용성 문제를 볼 수 있습니다. 아마도 이것을 허락 할 것입니다. 설명해 주셔서 감사합니다. – webdesserts

+0

HelperIn과 HelperOut은'Stdin'과'Stdout'의 (자신의) 인스턴스를 포함하는 클래스로'PrompSet '을 반환하는'default'를 구현할 수 없습니까? 'BufRead'와'Write'를 각각 처리합니다 (각 작업에 대한 스트림을 잠그고 잠금 가드에서 적절한 메소드 호출). 그것은 가능해야하지만, 내가 그것을 썼을 때,'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); 
    } 
}