Updated:

2 minute read

개요

  • 해당 참조자가 유효한 스코프
  • 주목적은 댕글링 참조자(dangling reference) 방지
  • 라이프 사이클을 변경하지 않음
  • 제네릭이 여러개의 인자에 타입을 연관 짓는 것 처럼 여러 개의 참조자 간에 라이프타임을 연관 짓는 것
  • 빌림 검사기(borrow checker)
    • 빌림이 유효한지를 결정하기 위해 스코프를 비교
  • 타입과 마찬가지로 명시하지 않아도 암묵적으로 추론되는 경우도 있고 명시해야하는 경우도 존재
  • 라이프타임 생략 규칙(lifetime elision rules)
    • 참조자에 대한 러스트의 분석 기능 내에 프로그래밍된 패턴들
    • 세 가지 규칙에 만족되지 않을 경우 컴파일 에러
      • 참조자인 입력 파라미터는 각각 고유한 라이프타임을 가짐
        • fn func(x: &i32) {} -> fn func<'a>(x: &'a i32) {}
      • 하나의 라이프타임 파라미터만 있을 경우 해당 라이프타임이 모든 출력 라이프타임 파라미터에 적용
        • fn func<'a>(x: &'a i32) -> &i32 {} -> fn func<'a>(x: &'a i32) -> &'a i32 {}
      • 메소드이고 입력 파라미터가&self 혹은 &mut self라면 self의 라이프타임이 모든 출력 라이프타임 파라미터에 적용
  • 구조체가 참조자를 가질 경우 라이프타임을 표시해야 함
  • 라이프타임 서브타이핑(subtyping)
    • 하나의 라이프타임이 다른 라이프타임보다 오래 사는 것을 보장
    • struct Parser<'c, 's: 'c> {
      • 's가 최소 'c만큼 오래 사는 것을 보장
  • 라이프타임 바운드
    • 제네릭 타입을 가리키는 참조자를 위한 라이프타임 명시
    • struct Ref<'a, T: 'a>(&'a T);
      • T내의 어떠한 참조자들도 최소한 'a만큼 오래 살 것임을 명시
    • struct StaticRef<T: 'static>(&'static T);
      • T가 오직 'static 참조자만을 갖거나 아무런 참조자도 없도록 제한
  • 트레잇 객체 라이프타임의 추론
    • 컴파일러는 어떻게 트레잇 객체의 라이프타임을 추론하며 언제 이들을 명시할 필요가 있는지


예제 1

  • 코드
    •  fn func<'a>(s1: &'a str, s2: &'a str) -> &'a str {
           if s1.len() > s2.len() {
               s1
           } else {
               s2
           }
       }
              
       fn main() {
           let s1 = String::from("a");
           let result;
           {
               let s2 = String::from("aa");
               result = func(s1.as_str(), s2.as_str());
              
               println!("{}", result);
           }
       }
      
  • 실행 결과
    •  aa
      


예제 2

  • 코드
    •  fn func<'a>(s1: &'a str, s2: &'a str) -> &'a str {
           if s1.len() > s2.len() {
               s1
           } else {
               s2
           }
       }
              
       fn main() {
           let s1 = String::from("a");
           let result;
           {
               let s2 = String::from("aa");
               result = func(s1.as_str(), s2.as_str());
           }
              
           println!("{}", result);
       }
      
  • 실행 결과
    •  error[E0597]: `s2` does not live long enough
         --> src/main.rs:14:36
          |
       13 |         let s2 = String::from("aa");
          |             -- binding `s2` declared here
       14 |         result = func(s1.as_str(), s2.as_str());
          |                                    ^^^^^^^^^^^ borrowed value does not live long enough
       15 |     }
          |     - `s2` dropped here while still borrowed
       16 |
       17 |     println!("{}", result);
          |                    ------ borrow later used here
              
       For more information about this error, try `rustc --explain E0597`.
       error: could not compile `test1` (bin "test1") due to previous error
      


예제 3

  • 코드
    •  struct Test<'a> {
           s: &'a String,
       }
              
       fn main() {
           let s1 = String::from("a");
           let test = Test { s: &s1 };
           println!("{}", test.s);
       }
      
  • 실행 결과
    •  a
      


예제 - 라이프타임 서브타이핑(subtyping)

  • 코드
    •  struct Context<'s>(&'s str);
              
       struct Parser<'c, 's: 'c> {
           context: &'c Context<'s>,
       }
              
       impl<'c, 's> Parser<'c, 's> {
           fn parse(&self) -> Result<(), &'s str> {
               Err(&self.context.0[1..])
           }
       }
              
       fn parse_context(context: Context) -> Result<(), &str> {
           Parser { context: &context }.parse()
       }
              
       fn main() {
           match parse_context(Context("abc")) {
               Ok(()) => println!("Ok"),
               Err(e) => println!("{}", e),
           }
       }
      
  • 실행 결과
    •  bc
      


예제 - 라이프타임 바운드

  • 코드
    •  #[derive(Debug)]
       struct Ref<'a, T: 'a>(&'a T);
              
       #[derive(Debug)]
       struct StaticRef<T: 'static>(&'static T);
              
       fn main() {
           let i = 1;
           let s = Ref(&i);
           println!("1 : {:?}", s);
              
           static I: i32 = 1;
           let s = StaticRef(&I);
           println!("2 : {:?}", s);
       }
      
  • 실행 결과
    •  1 : Ref(1)
       2 : StaticRef(1)