2014-12-14 2 views
2

저는 녹에 익숙하지 않아 전체 소유권/개념을 이해하는 것이 어렵습니다. ... 모든 공식 가이드를 읽은 후에도.체이닝 함수는 중간 변수를 사용하여 vs를 호출합니다.

왜 다음 코드가 문제없이 컴파일됩니까?

use std::io; 

fn main() { 
    let mut input = io::stdin(); 
    let mut lock = input.lock(); 
    let mut lines_iter = lock.lines(); 

    for line in lines_iter { 
     let ok = line.ok(); 
     let unwrap = ok.unwrap(); 
     let slice = unwrap.as_slice(); 

     println!("{}", slice); 
    } 
} 

...하지만 이것은 아닙니까?

use std::io; 

fn main() { 
    let mut lines_iter = io::stdin().lock().lines(); 

    for line in lines_iter { 
     let slice = line.ok().unwrap().as_slice(); 
     println!("{}", slice); 
    } 
} 

두 가지 코드 샘플은 똑같이 수행하고 있습니다. 유일한 차이점은 첫 번째 매개 변수는 중간 매개 변수를 사용하고 두 번째 매개 변수는 함수 호출을 연결한다는 것입니다. 두 번째를 컴파일 할 때

, 그것은 정직하게, 나는이 컴파일러는 나에게 말하려고 무엇인지 전혀 모른다

- error: borrowed value does not live long enough 
- note: reference must be valid for the block at 
- note:...but borrowed value is only valid for the statement 
- help: consider using a `let` binding to increase its lifetime 

의 많은 나에게 악. 나는 평생 문제가 있다는 것을 이해합니다. 하지만 왜?

두 코드 샘플의 차이점은 무엇입니까? 왜 그리고 어떻게 그것이 평생 동안 영향을 미치고 있습니까?

답변

6

중간 변수를 정의하면 중간 값의 수명이 연장됩니다. 임시 값 (예 : io::stdin()io::stdin().lock()io::stdin().lock().lines())은 이동하지 않는 한 명령문의 끝 부분에 존재하지 않습니다 (io::stdin().lock()의 경우). let mut lines_iter = io::stdin().lock().lines();에서

:; 수명은 소스에 elided 있었기 때문에 설명서의 <'a>이 표시되지 않는

  • io::stdin() 새로운 Stdin
  • .lock()Stdin가 참조하는 (새로운 StdinLock<'a>를 돌려줍니다)
  • .lines() (잠금의 소유권을 취하는) Lines<StdinLock<'a>>을 리턴합니다.

리턴 유형 .lock()의 유효 기간 매개 변수는 잠금이 Stdin 오브젝트에서 차용되었음을 나타냅니다. 어떤 물건에서 빌릴 때, 그 물건은 적어도 빌릴 때까지 살아야합니다. 그러나 함수의 끝까지 지속되는 변수를 갖기 위해 노력하고 있지만 (명령문의 끝에서 삭제 될 객체에서 빌려옵니다 (io::stdin()은 임시 값이기 때문에).

기록 참고 사항 :이 질문에 원래 질문했을 때 .lines()은 자물쇠에서 빌릴 수 있습니다. 이제 .lines()은 잠금의 소유권을 대신 사용합니다. 즉, 지금은 io::stdin() 만 변수에 바인딩해야합니다. 더 이상 input.lock()의 결과를 바인딩 할 필요가 없습니다.

+0

좋습니다. "수명 매개 변수가있는 중간 값이 문 끝에서 중단됨"을 수락합니다. 수명에 대해 조금 더 읽어야합니다. 하지만 : "중간 값 사용"이 실제로 이것을 해결하는 방법입니까? ... 중복 해결 방법과 비슷합니다. – forgemo

+1

지금은 아마도 추론 알고리즘이 수명까지 연장 될 수 있지만 기능적이지 않은 주된 이유가 있습니다. 특히 암시 적보다 낫습니다. –

+1

자바, 스칼라, haskell 또는 다트 등 주요 "상위 수준"언어에서 오는 ....이 행동은 나에게 매우 "방해"것 같습니다. 이런 식으로 좀 더 복잡한 코드를 개발하는 것이 얼마나 성가신 일인지 상상할 수 없습니다. 조만간이 문제를 해결하기 바랍니다. 도와 주셔서 감사합니다! – forgemo

1
           XXXXXXXXXXX 
             XXXXXXXX   XXXXXXX 
            XX Gets destroyed after X 
            X end of statement  XX 
            XX if not binded  XX 
          +-----+ XXXXXX  XXXXXXXXXX 
          |    XXXXXXXX 
          v 

+-------------+  +-------------+  +----------------+ 
| .lock() |  | io::stdin()|  |    | 
|    +-------->   +--------> Global  | 
| Lock  |  |StdinReader   |Stdin Buffer | 
|    |  |    |  |    | 
|    |  |    |  |    | 
+------^------+  +-------------+  +----------------+ 
     | 
     | 
     | 
     | 
+------+-------+ 
| .lines() | 
|    | 
| Iterator | 
|    | 
|    | 
+--------------+ 

그래서 녹이

1

난 그냥 몇 가지 세부 사항은 현재 다르기 때문에 나는이 질문을 다시 거라고 생각 허용하지 않을 것입니다.(솔직히 말해서, 난 그냥이 나 자신을 이해하지 않기 때문에 나는이 물건에 파고 내 결과를 기록하기로 결정했다.)

우리는이 코드로 시작 : 여기

use std::io::{stdin, BufRead}; 

fn main() { 
    for l in stdin().lock().lines() { 
     println!("{}", l.unwrap()); 
    } 
} 

것은 무엇 컴파일러 대답했습니다이 여전히 하시다하지 않습니다

fn main() { 
    let lock = stdin().lock(); 
} 

:

t.rs:4:14: 4:21 error: borrowed value does not live long enough 
t.rs:4  for l in stdin().lock().lines() { 
        ^~~~~~~ 
t.rs:4:5: 6:6 note: reference must be valid for the destruction scope surrounding statement at 4:4... 
t.rs:4  for l in stdin().lock().lines() { 
t.rs:5   println!("{}", l.unwrap()); 
t.rs:6  } 
t.rs:4:5: 6:6 note: ...but borrowed value is only valid for the statement at 4:4 
t.rs:4  for l in stdin().lock().lines() { 
t.rs:5   println!("{}", l.unwrap()); 
t.rs:6  } 
t.rs:4:5: 6:6 help: consider using a `let` binding to increase its lifetime 

이의 간단 뭔가를 해보자 rk이고 오류는 매우 비슷합니다. 이것이 작동하지 않는다는 사실은 문제가 stdin() 전화와 함께 있음을 말해줍니다.

t.rs:4:16: 4:23 error: borrowed value does not live long enough 
t.rs:4  let lock = stdin().lock(); 
         ^~~~~~~ 
t.rs:3:11: 5:2 note: reference must be valid for the block at 3:10... 
t.rs:3 fn main() { 
t.rs:4  let lock = stdin().lock(); 
t.rs:5 } 
t.rs:4:5: 4:31 note: ...but borrowed value is only valid for the statement at 4:4 
t.rs:4  let lock = stdin().lock(); 
      ^~~~~~~~~~~~~~~~~~~~~~~~~~ 
t.rs:4:5: 4:31 help: consider using a `let` binding to increase its lifetime 

유형을 살펴 보겠습니다. .lock 방법이있다 stdin 반환 Stdin :

fn lock(&self) -> StdinLock 

메서드의 반환 형식이 StdinLock,하지만 당신은 그 선언을 보면, 당신이 평생 매개 변수를 사용하는 것을 볼 수 있습니다 :

pub struct StdinLock<'a> { 
    // some fields omitted 
} 

생략 된 필드가이 매개 변수의 이유이며, sources을 참조하여 MutexGuard이 있음을 알게되고, 유효 기간이 가드 내부에 저장된 값의 유형에 적용됩니다. 그러나 그것은 실제로 전혀 중요하지 않습니다.

fn lock<'a>(&'a self) -> StdinLock<'a> /* Self = Stdin */ 

겠어요 - : 포인트가 포함 lifetime elision 있다는 것을 의미이 유효 기간 파라미터, 및 lock 방법 선언 인이 실제로 있다는 로컬 lock 변수의 유형은 StdinLock<'a>입니다. 이 'a 유형의 매개 변수는 StdinLock 내부에 최소한 'a에 유효해야하는 참조가 있음을 의미합니다. 반면에 lock이 우리 함수의 지역 변수라는 사실로부터 우리는 범위가이 함수의 본문임을 알고 있고 그 유형이 StdinLock<'a>이라는 사실로부터 컴파일러는 'a이 본문에 해당하는 범위라고 결론을 내립니다 함수의.

우리가 깨달을 때이 시점에서의 그는 유형 값에 의해 반환라고 우리에게 이야기하기 때문에, 전체 함수 본문 살아 있어야한다에 전달 가져옵니다 self 인수 유효 할 .lock()에 호출 .lock()은 그 일부에 대한 일부 참조를 유지합니다. 그러나 우리가 명시 적으로을 사용하면 let을 사용하면 수명이 길어지기 때문에 계산서가 끝나면 즉시 폐기됩니다. 발생

use std::io::{stdin, BufRead}; 

fn main() { 
    let stdin = stdin(); 
    for l in stdin.lock().lines() { 
     println!("{}", l.unwrap()); 
    } 
} 

가 작동 :

우리는 가진 끝.

항상 그렇듯이 모든 것이 소유권 문제로 귀결됩니다. .lock()에서 반환 된 값은 호출 된 항목의 소유권을 가지지 않지만 (이 경우에는 stdin()의 결과 임) 참조를 유지합니다.즉, stdin()의 결과를 유지하는 책임을 맡고 누군가가 당신을 (그리고 나)해야한다는 것을 의미합니다. 다른 옵션은 없으므로 누군가가 당신이되어야합니다.).

한편

, .lines()의 유형이있다 :

fn lines(self) -> Lines<Self> /* Self = StdinLock */ 

당신은 self을 소모하기 때문에 값에서 반환 우리가 명시 적으로 .lock()의 결과를 결합하지 않아도 볼 수 있듯이 .lines()은 자물쇠의 소유권을 취하므로 필요에 따라 그것을 유지 한 다음 파기해야 할 책임이 있습니다.

관련 문제