Rust - 라이프타임(lifetime)은 또 뭡니까?

프로필 사진mingke

Rust what is lifetime?

목차

라이프타임

파이썬 개발자에게 어쩌면 Rust는 취미로 공부하기엔 너무 빡센 언어인것 같기도 합니다. 라이프타임(lifetime)까지 진도를 나왔는데 어려워서 블로그에 정리하며 공부해보려고 합니다.

아래 문서를 보면서 하고 있습니다.

라이프타임으로 참조자의 유효성 검증하기 - The Rust Programming Language

라이프타임메모리 안정성을 보장하기 위한 Rust의 여러 장치들 중 하나인 것 같습니다. 제네릭의 한 종류이며, 어떤 참조자가 얼마나 오랫동안 유효한지 컴파일러가 추적하고 검증하는 방식입니다.

Rust는 메모리 관리를 컴파일 타임에 진행하는데 가비지 컬렉터도 없는데 안전한 메모리 관리와 성능을 낼 수 있는 것은 이런 장치들 때문입니다.

모든 참조자는 라이프타임을 가진다 이 문장을 기억하고 진행합니다.

라이프타임의 주목적은 Dangling Reference 방지라고 합니다. 이제 다음과 같은 코드는 너무 쉽죠

fn main() {
    let r;
    {
        let x = 5;
        r = &x;
    }
    // r이 x를 참조하고 있는데 스코프를 벗어나서 메모리 해제 되었는데
    // r을 어떻게 출력하냐?
    println!("r: {}", r);
}
 
// 이게 옳게된 코드죠
fn main() {
    let x = 5;            // ----------+-- 'b
                          //           |
    let r = &x;           // --+-- 'a  |
                          //   |       |
    println!("r: {}", r); //   |       |
                          // --+       |
}                         // ----------+

라이프 타임 사용 방법

위 코드에서 잠깐 보시다시피 ‘a ’b 이런 식으로 를 써서 사용합니다. 관습적으로 a 부터 시작하는 것 같습니다.

보통 함수나 struct를 정의할 때 사용합니다.

  • 함수에서 사용
// 이게 왜 컴파일이 안될까?
// 정답은 x나 y나 어떤게 반환될 지 모르기 때문에
// 컴파일러가 매개변수들의 라이프타임을 알 수 없음
fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
 
// 따라서 컴파일러가 알 수 있도록 라이프타임을 명시적으로 지정해주어야 합니다.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
 
// 이렇게 쓸 수 있겠습니다.
&'a i32
&'a mut str
&'a Vec<i32>
...
  • struct에서 사용
    • 이건 좀 이해 하느라 애를 먹었네요.
// part필드에 라이프타임이 명시되어야 하는 이유
// 입력받는 필드 part는 입력된 참조자 필드보다 오랫동안 살아 있을 수 없기 떄문입니다.
struct ImportantExcerpt<'a> {
    part: &'a str,
}
// novel에서 나온 first_sentence를 참조로 받기 때문에
// novel보다 라이프타임이 길수 없다고 명시해주어야 하는 것입니다.
fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    let I = ImportantExcerpt {
        part: first_sentence,
    };
}

라이프타임 생략(lifetime elision)

라이프타임을 컴파일러가 추론할 수 있다면 생략이 가능합니다. 러스트 처음 공부할 때 예제에는 라이프타임 같은거 생략되어 있었어요.

컴파일러가 라이프타임이 명시되어 있지 않을 때 추론하는 규칙은 3가지라고 합니다.

  • 규칙1: 각각 매개변수에 개별 라이프타임 대입
  • 규칙2: 매개변수가 1개면 출력에도 같은 라이프타임 대입
  • 규칙3: 매개변수가 여러 개인데 그중에 &self, &mut self 가 있다면 출력에도 self와 같은 라이프타임 대입

위와 같은 이유 때문에 이전 예시에서 사용한 longest 는 라이프타임 추론이 불가 했던 것입니다.

// 입력 참조자가 하나라서 컴파일러가 라이프타임을 자동으로 추론 가능합니다.
fn get_string(s: &str) -> &str {
    s
}

정적 라이프타임

static 라이프타임은 꽤 간단했는데요. 라이프타임이 프로그램 전체 생애 동안 살아있는 경우 사용합니다.

'static 이렇게 사용합니다.

let s: &'static str = "blah blah"

static을 사용하기 전엔 이게 꼭 필요한 것인지 한 번 더 고민하라고 합니다.

마무리

매서드에 사용하기, 제네릭 타입, 트레이트 바운드에 사용 등은 이 정도 정리해보니 알 것 같아서 생략했습니다.

현재 파이썬 백엔드 부트캠프에서 서브강사를 하고 있는데, 최근 훈련생분들 클래스 복습을 도와주면서 “개념적으론 알겠는데 코드를 써보려면 잘 모르겠다” 라는 이야기를 들었습니다. 제가 지금 Rust에서 이 현상을 겪고 있는지도 모르겠습니다. 그래도 화이팅.

Loading...