The Book의 다음과 같은 코드를 보자.
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() > b.len() {
a
} else {
b
}
}
Rust의 모든 reference 값들은 lifetime을 갖고 있다. 이는 dangling reference를 막기 위함인데, 특정 시점에 메모리에서 해제된 값을 가리키는 레퍼런스는 버그를 유발하기 때문이다.
longest
함수는 레퍼런스를 리턴하기 때문에, 컴파일러는 리턴되는 이 레퍼런스가 어느 시점까지 유효한지 그 정보가 더 필요하다. 위의 예제의 경우, 매개변수로 받는 레퍼런스를 그대로 리턴하기 때문에, 리턴된 레퍼런스가 사용되는 시점에도 두 매개변수는 유효해야만 함수의 동작이 보장된다.
예를 들어, 아래와 같은 경우를 보자.
let result;
let a = String::from("Hi");
{
let b = String::from("Hello");
result = longest(&a, &b);
}
println!("{result}");
이 경우, b
의 값은 println!
이 실행되는 시점에 이미 메모리에서 해제된다. 따라서 result
는 유효하지 않은 레퍼런스를 갖게 된 셈이다. 이를 막기 위해 lifetime을 코드에 명시하여 컴파일러가 레퍼런스의 수명을 분석할 수 있게 된다.
이제 본론을 얘기할 차례다. 아래와 같은 코드는 어떨까?
let result;
let a = "Hi";
{
let b = "Hello";
result = longest(a, b);
}
println!("{result}");
얼핏 보면 마찬가지로 유효하지 않을 것 같지만, String literal은 'static
수명을 갖기 때문에, scope를 벗어나도 유효하다. 따라서 별다른 에러 없이 컴파일되고 실행된다.