비 어휘 수명이란 무엇입니까?


답변:


139

그것은 비 어휘 수명이 무엇인지 이해가 무엇인지 이해하는 가장 쉬운 어휘 수명이다. 비 어휘 수명이 존재하기 전의 Rust 버전에서는이 코드가 실패합니다 :

fn main() {
    let mut scores = vec![1, 2, 3];
    let score = &scores[0];
    scores.push(4);
}

Rust 컴파일러는 변수 scores가 차용 한 것을 인식 score하므로 다음과 같은 추가 변형을 허용하지 않습니다 scores.

error[E0502]: cannot borrow `scores` as mutable because it is also borrowed as immutable
 --> src/main.rs:4:5
  |
3 |     let score = &scores[0];
  |                  ------ immutable borrow occurs here
4 |     scores.push(4);
  |     ^^^^^^ mutable borrow occurs here
5 | }
  | - immutable borrow ends here

그러나 사람은이 예제가 지나치게 보수적이라는 것을 쉽게 알 수 있습니다. scoreis never used ! 문제는 scoresby 의 차용 score어휘 적이라는 것입니다. 이것은 그것이 포함 된 블록의 끝까지 지속됩니다.

fn main() {
    let mut scores = vec![1, 2, 3]; //
    let score = &scores[0];         //
    scores.push(4);                 //
                                    // <-- score stops borrowing here
}

비 어휘 수명은 컴파일러가이 수준의 세부 사항을 이해하도록 개선하여이 문제를 해결합니다. 컴파일러는 이제 차용이 필요한시기를보다 정확하게 알 수 있으며이 코드는 컴파일됩니다.

비어 휘적 삶의 놀라운 점은 일단 활성화되면 아무도 그것에 대해 생각하지 않을 것 입니다. 그것은 단순히 "러스트가하는 일"이되고 모든 것이 (희망적으로) 작동 할 것입니다.

어휘 수명이 허용 된 이유는 무엇입니까?

Rust는 알려진 안전한 프로그램 만 컴파일하도록 허용합니다. 그러나 불가능하다 정확하게 할 수 있도록 단지 안전 프로그램 및 안전하지 않은 것들을 거부합니다. 이를 위해 Rust는 보수적 인 측면에서 오류가 있습니다. 일부 안전한 프로그램은 거부됩니다. 어휘 수명이 이에 대한 한 가지 예입니다.

블록에 대한 지식은 "사소한"반면 데이터 흐름에 대한 지식은 적기 때문에 어휘 수명은 컴파일러에서 구현하기 훨씬 쉬웠습니다. 컴파일러 는 "중간 수준 중간 표현"(MIR)을 도입하고 사용하기 위해 다시 작성해야했습니다 . 그런 다음 AST (추상 구문 트리) 대신 MIR을 사용하도록 차용 검사기 (일명 "borrowck")를 다시 작성해야했습니다. 그런 다음 차입 검사기의 규칙을 세분화해야했습니다.

어휘 수명이 항상 프로그래머에게 방해가되는 것은 아니며, 성가 시더라도 어휘 수명을 처리 할 수있는 많은 방법이 있습니다. 많은 경우에 추가 중괄호 또는 부울 값을 추가해야했습니다. 이로 인해 Rust 1.0이 출시되었고 비 어휘 수명이 구현되기 전 수년 동안 유용했습니다.

흥미롭게도 어휘 수명 때문에 특정 좋은 패턴이 개발되었습니다. 저에게 가장 좋은 예 entry패턴 입니다. 이 코드는 비 어휘 수명 전에 실패하고 함께 컴파일됩니다.

fn example(mut map: HashMap<i32, i32>, key: i32) {
    match map.get_mut(&key) {
        Some(value) => *value += 1,
        None => {
            map.insert(key, 1);
        }
    }
}

그러나이 코드는 키의 해시를 두 번 계산하기 때문에 비효율적입니다. 어휘 수명으로 인해 생성 된 솔루션 은 더 짧고 효율적입니다.

fn example(mut map: HashMap<i32, i32>, key: i32) {
    *map.entry(key).or_insert(0) += 1;
}

"비어 휘적 수명"이라는 이름은 나에게 맞지 않습니다.

값의 수명은 값이 특정 메모리 주소에 유지되는 시간 범위입니다 ( 더 자세한 설명 은 값과 해당 값에 대한 참조를 동일한 구조체에 저장할 수없는 이유는 무엇입니까? 참조 ). 비 어휘 수명으로 알려진 기능 은 값의 수명을 변경 하지 않으므로 수명을 비 어휘로 만들 수 없습니다. 이는 해당 값의 차입을 더 정확하게 추적하고 확인합니다.

기능의 더 정확한 이름은 "비 어휘 차용 "일 수 있습니다. 일부 컴파일러 개발자는 기본 "MIR 기반 차용"을 참조합니다.

비어 휘적 수명은 그 자체 로 "사용자를 향한"기능을 의도하지 않았습니다 . 그들은 우리가 부재로 인해 얻는 작은 종이 컷 때문에 우리 마음 속에서 대부분 커졌습니다. 그들의 이름은 대부분 내부 개발 목적으로 사용되었으며 마케팅 목적으로 변경하는 것은 결코 우선 순위가 아니 었습니다.

예,하지만 어떻게 사용합니까?

Rust 1.31 (2018-12-06에 릴리스 됨)에서는 Cargo.toml에서 Rust 2018 에디션을 옵트 인해야합니다.

[package]
name = "foo"
version = "0.0.1"
authors = ["An Devloper <an.devloper@example.com>"]
edition = "2018"

Rust 1.36부터 Rust 2015 에디션은 비 어휘 수명도 지원합니다.

비 어휘 수명의 현재 구현은 "마이그레이션 모드"에 있습니다. NLL 차용 검사기가 통과하면 컴파일이 계속됩니다. 그렇지 않은 경우 이전 차용 검사기가 호출됩니다. 이전 차용 검사기가 코드를 허용하면 경고가 인쇄되어 향후 Rust 버전에서 코드가 손상 될 가능성이 있으며 업데이트해야 함을 알려줍니다.

Rust의 야간 버전에서는 기능 플래그를 통해 강제 중단에 옵트 인 할 수 있습니다.

#![feature(nll)]

컴파일러 플래그를 사용하여 NLL의 실험적 버전을 선택할 수도 있습니다 -Z polonius.

비 어휘 수명으로 해결 된 실제 문제 샘플


12
아마도 반 직관적으로 비언어적 수명은 변수의 수명이 아니라 차입의 수명에 관한 것이라는 점을 강조 할 가치가 있다고 생각합니다. 또는 달리 말하면 Non-Lexical Lifetimes는 변수의 수명을 차용의 수명과 장식하는 것에 관한 것입니다 ... 내가 틀리지 않는 한? (그러나 소멸자가 실행될 때 NLL이 변경되지 않는다고 생각합니다)
Matthieu M.

2
" 흥미롭게도, 어휘 수명 때문에 특정 좋은 패턴이 개발되었습니다. "-그렇다면 NLL의 존재가 미래의 좋은 패턴을 식별하기 훨씬 더 어렵게 만들 위험이 있다고 생각합니다.
eggyal

1
@eggyal 확실히 가능성이 있습니다. 일련의 제약 조건 내에서 설계하면 (임의적 일지라도!) 새롭고 흥미로운 설계로 이어질 수 있습니다. 이러한 제약이 없으면 기존의 지식과 패턴으로 되돌아가 새로운 것을 찾기 위해 배우거나 탐색하지 않을 수 있습니다. 즉, 아마도 누군가는 "아, 해시가 두 번 계산되고 있습니다. 제가 수정할 수 있습니다."라고 생각하고 API가 생성 될 것이라고 생각할 것입니다. 그러나 사용자가 처음에 API를 찾기가 더 어려울 수 있습니다. clippy 와 같은 도구가 그 사람들에게 도움이 되기를 바랍니다 .
Shepmaster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.