[Rust] 라이프타임
Updated:
개요
- 해당 참조자가 유효한 스코프
- 주목적은 댕글링 참조자(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)
-