모든 변수와 함수는 생명주기를 가진다. 컴파일러는 변수와 함수 및 레퍼런스의 생명주기를 판단하고 시간적 관점에서 논리적 오류가 없는지 확인한다.
Lifetime
은 시간적 측면에서의 Generic이라고 할 수 있다. 일반적으로 Generic은 u32, i32, 공통의 타입을 나타내는 T 등 타입을 미리 정해줄 수 있지만, Lifetime
은 컴파일 될 때 결정된다.
fn main() {
{
let r; // ---------+-- 'a
// |
{ // |
let x = 5; // -+-- 'b |
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // |
} // ---------+
}
x
의 값을 참조하는 r
을 바깥쪽 scope에서 출력하려고 한다. 그런데 x
의 lifetime이 안쪽 scope를 벗어나면 끝나기 때문에, r
은 출력될 수 없다. 따라서 r
의 생명주기와 x
의 생명주기에서 논리적 오류가 발생하며 아래처럼 사용해야 한다.
fn main() {
{
let x = 5; // ----------+-- 'b
// |
let r = &x; // --+-- 'a |
// | |
println!("r: {}", r); // | |
// --+ |
} // ----------+
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
}
fn longest<'a>(x: &'a str, y: &'b str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
lifetime에서 중요한 것은 언제 시작했냐가 아니라 수명이 언제 끝나는지다. string1
과 string2
의 lifetime 시작은 다르지만(string1이 먼저 시작), 같은 함수에서 리턴되는 시점은 같다.
즉, string1
과 string2
의 생명주기가 동시에 끝나기 때문에 &'a str
또는 &'b str
로 리턴하면 오류가 나며 아래 처럼 하나로 통일해줘야 한다.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
생명주기가 모두 다르다면, 'a
는 어떤 변수의 생명주기를 따라갈까? 생명주기가 다를 때는 가장 짧은 생명주기를 공통분모로 가져간다. 이해를 돕기 위해 벤 다이어그램으로 나타내봤다. 예제 코드를 조금 변경해서 Result, String2, String1 순으로 생명주기가 짧다면(Result의 생명주기가 가장 짧다고 가정) 공통분모가 되는 Result의 생명주기를 'a
로 가져간다.