개요
- 해당 참조자가 유효한 스코프
- 주목적은 댕글링 참조자(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> {
- 라이프타임 바운드
- 제네릭 타입을 가리키는 참조자를 위한 라이프타임 명시
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);
}
}
- 실행 결과
예제 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);
}
- 실행 결과
예제 - 라이프타임 서브타이핑(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),
}
}
- 실행 결과
예제 - 라이프타임 바운드
- 코드
-
#[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)