Rust는 메모리 안전성을 보장하기 위해 소유권과 함께 라이프타임이라는 개념을 도입합니다. Rust는 라이프타임을 자동으로 추론하지만, 필요한 경우 참조가 유효한 범위인 라이프타임을 컴파일러에게 명시적으로 알려주어 댕글링 참조(dangling reference)를 방지합니다.
댕글링 참조는 메모리가 해제된 이후에도 해당 메모리 위치를 가리키는 참조를 말합니다. 즉, 참조자가 가리키는 메모리가 더 이상 유효하지 않은 상태를 의미합니다. 이러한 댕글링 참조는 프로그램 충돌이나 데이터 손상을 유발할 수 있는 잠재적인 위험을 가지고 있습니다. |
1. 라이프타임이 필요한 이유
다음 코드를 보세요.
fn main() {
let r;
{
let x = 5;
r = &x; // ❌ x는 여기서 소멸됨
}
println!("{}", r); // 컴파일 에러!
}
- x는 내부 블록에서 생성되어 그 블록이 끝나면 해제되는데,
- r은 그보다 오래 살아 남으므로 유효하지 않은 x를 참조가 됩니다. 이것을 댕글링 참조라고 합니다.
- Rust는 댕글링 참조를 컴파일 타임에 잡아냅니다.

2. 기본 라이프타임 추론
위와 같이 대부분의 경우, Rust는 라이프타임을 자동으로 추론합니다. 그러나 두 개 이상의 참조가 관련되면 명시적 표기가 필요합니다.
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let s1 = String::from("hello");
let s2 = String::from("world");
let result = longest(&s1, &s2);
println!("The longest string is: {}", result);
}
이 함수는 x, y 두 개 중 어떤 것이 수명이 긴지 알 수가 없어서 컴파일 에러 발생 가능성이 있으므로 라이프타임 명시가 필요합니다.

3. 라이프타임 명시하기
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
- ‘a는 라이프타임 매개변수입니다.
- x, y, 반환값에 모두 ‘a를 붙여 “동일한 수명을 가진다”는 것을 명시해야 합니다.
- 입력 참조가 없어진 상태에서 반환값이 살아 있으면, 다시 말해 반환값이 입력 참조보다 수명이 길면 오류가 발생하기 때문입니다.
4. 구조체에서의 라이프타임
아래와 같이 구조체 필드의 형식이 String인 경우는 Lifetime을 명시하지 않아도 되나,
struct Excerpt {
part: String,
}
fn main() {
let text = String::from("Rust는 안전하다.");
let excerpt = Excerpt { part: text };
println!("{}", excerpt.part);
}
구조체 필드의 타입이 참조인 경우, 여기서는 &str(문자열 슬라이스), 반드시 라이프타임을 명시해야 합니다.
라이프타임 지정시 구조체명 오른쪽의 <> 안에 라이프타임 매개변수를 ‘a라고 명시했고, 필드의 형식에도 라이프타임 매개변수 ‘a를 추가했습니다.
struct Excerpt<'a> {
part: &'a str,
}
fn main() {
let text = String::from("Rust는 안전하다.");
let excerpt = Excerpt { part: &text[0..4] };
println!("{}", excerpt.part);
}
- Excerpt<‘a>는 part 필드가 ‘a 동안 유효함을 의미합니다. 이 말은 결국, “Excerpt 구조체는 자신이 가리키는 문자열(&str)보다 더 오래 살아있을 수 없다”는 제약을 의미하며, “Excerpt 구조체 내의 part 필드는 text가 살아 있는 동안만 유효하다”는 의미도 됩니다.
- 출력 결과는 Rust입니다.
5. 함수 내 수명 충돌 예시
가. 문제
fn return_ref<'a>(x: &'a str, y: &str) -> &'a str {
// x // ✔ OK
return y; // ❌ y는 'a보다 짧은 수명일 수 있음
}
fn main() {
let s1 = String::from("hello");
let s2 = String::from("world");
let r = return_ref(&s1, &s2);
println!("r = {}", r);
}
- 함수의 반환값이 ‘a 수명을 가진 참조여야 하므로,
라이프타임 매개변수 ‘a를 갖지 않은 y는 반환할 수 없습니다.
나. 해결
fn return_ref<'a>(x: &'a str, y: &'a str) -> &'a str {
// x // ✔ OK
return y; // ❌ y는 'a보다 짧은 수명일 수 있음
}
- y를 출력하고자 하는 경우는 y: &str을 y: &’a str로 수정해야 합니다.
- x를 출력하려고 하는 경우는 x 앞의 주석을 제거하고, return y;를 주석 처리하면 됩니다.
6. ‘static 라이프타임
가. 반환 형식이 String인 경우 문제 없음
아래와 같이 반환 타입이 String인 경우는 문제가 없는데, 출력이 잘 됩니다.
fn get_static_str() -> String {
String::from("나는 프로그램 전체에서 살아있다!")
}
fn main() {
let msg = get_static_str();
println!("{}", msg);
}
나. 반환 형식이 &str인 경우 문제 있음
아래와 같이 반환 형식이 &str인 경우는 실행 시
fn get_static_str() -> &str {
"나는 프로그램 전체에서 살아있다!"
}
fn main() {
let msg = get_static_str();
println!("{}", msg);
}
“라이프타임 매개변수 지정이 기대된다”고 하면서 빌려올 값이 없으므로 ‘static을 추가하거나, 반환 형식을 String으로 하라고 제안합니다.

'static
은 프로그램 전체 수명을 의미- 문자열 리터럴 등 컴파일 타임 상수에 주로 사용
다. 수정 코드
&str을 &’static str로 수정하면 문제 없이 출력됩니다.
fn get_static_str() -> &'static str {
"나는 프로그램 전체에서 살아있다!"
}
fn main() {
let msg = get_static_str();
println!("{}", msg);
}

7. 요약 정리
개념 | 설명 |
---|---|
라이프타임 ‘a | 참조가 얼마나 오래 유효한지 표시하는 표기 |
함수 수명 명시 | 여러 참조 중 어느 것이 반환되는지(라이프타임)를 명확히 지정 |
구조체 + 참조 | 참조 필드가 있으면 명시적 라이프타임 필요 |
‘static | 프로그램 전체 수명 (전역 문자열 등) |
오류 예방 목적 | 댕글링 참조를 컴파일 타임에 방지함 |
8. 결론
라이프타임은 Rust의 가장 강력하면서도 헷갈릴 수 있는 개념입니다. 하지만 “누가 누구보다 오래 살아야 하는가”를 생각하면서 설계하면, 오히려 런타임 오류 없이 안전한 코드를 보장받을 수 있습니다.