
안녕하세요.
오늘은 Rust의 Generic Lifetime에 대해 알아보도록 하겠습니다.
서론
Rust에서는 Lifetime 개념이 중요합니다
Rust의 메모리 안정성을 지탱하는 매우 중요한 개념이라 생각합니다
이번 글에서는 다음 내용을 중심으로 정리해보겠습니다
- Lifetime은 왜 필요한가?
- Generic Lifetime은 언제 사용되는가?
- 함수와 구조체에서 어떻게 사용하는가?
- Lifetime Elision은 무엇인가?
- 'static은 무엇인가?
- 흔히 하는 오해는 무엇인가?
Lifetime
왜 필요한가?
Rust에서 가장 중요한 개념 중 하나는 소유권(Ownership) 입니다.
그리고 소유권과 함께 따라오는 개념이 바로 Borrowing(참조) 입니다.
fn main() {
let s = String::from("hello");
let r = &s;
println!("{}", r);
}
여기서 r은 s를 빌립니다.
Rust 컴파일러는 컴파일 타임에 다음을 확인합니다.
r이 s보다 오래 살아남지 않는가?
즉, 참조가 원본보다 오래 존재하지 않는지를 검사합니다.
이 “얼마나 오래 살아있는가”라는 개념을 표현하는 것이 바로 Lifetime입니다.
필요한 상황
다음 함수를 보겠습니다.
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() { x } else { y }
}
이 코드는 컴파일되지 않습니다.
에러 메시지는 다음과 같습니다.
error[E0106]: missing lifetime specifier
--> src\\main.rs:7:33
|
7 | fn longest(x: &str, y: &str) -> &str {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
help: consider introducing a named lifetime parameter
|
7 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
| ++++ ++ ++ ++
컴파일러는 이렇게 생각합니다.
- 반환되는 참조는 x일 수도 있고
- y일 수도 있다
- 그런데 반환값이 누구의 수명을 따르는지 명확하지 않다
그래서 우리는 Lifetime Generic을 명시해야 합니다
기본 문법
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
‘a가 바로 Lifetime Generic 입니다
이 코드는 이렇게 읽을 수 있습니다.
- x와 y는 동일한 lifetime 'a를 가진다.
- 반환값도 동일한 lifetime 'a를 가진다.
in 구조체
Lifetime은 구조체에서도 필요합니다
struct Holder<'a> {
value: &'a str,
}
이 코드는 다음과 같은 의미를 가집니다.
Holder는 lifetime 'a를 가지며,
내부의 참조는 그 lifetime을 따른다.
사용 예
fn main() {
let s = String::from("hello");
let h = Holder { value: &s };
println!("{}", h.value);
}
- 여기서 Holder는 s보다 오래 살 수 없습니다
Lifetime Elision (생략 규칙)
다행히 모든 경우에 Lifetime을 명시해야 하는 것은 아닙니다.
예를 들어,
fn first_word(s: &str) -> &str {
s
}
이 코드는 lifetime을 명시하지 않았지만 정상적으로 컴파일됩니다.
그 이유는 Rust에 Lifetime Elision 규칙이 있기 때문입니다.
대표적인 규칙은 다음과 같습니다.
- 각 입력 참조는 고유한 lifetime을 가진다.
- 입력이 하나라면, 출력은 그 lifetime을 따른다.
- 메서드에서 &self가 있다면, 출력은 self의 lifetime을 따른다.
즉, 단순한 경우에는 컴파일러가 자동으로 추론해 줍니다.
Static Lifetime
'static은 자주 등장하는 특별한 lifetime입니다
let s: &'static str = "hello";
문자열 리터럴은 프로그램 종료 시점까지 메모리에 존재합니다.
→ 즉, ‘static은 프로그램 전체 실행 동안 유효한 lifetime 입니다.
흔히 하는 오해
Lifetime을 붙인다고 수명이 늘어나지 않습니다
다음 코드는 컴파일되지 않습니다
fn fake<'a>(x: &'a str) -> &'a str {
let s = String::from("hi");
&s
}
error[E0515]: cannot return reference to local variable `s`
--> src\\main.rs:14:5
|
14 | &s
| ^^ returns a reference to data owned by the current function
이유는 간단합니다.
- s는 함수가 끝나면 사라집니다.
- 그 참조를 밖으로 반환할 수 없습니다.
Lifetime은 수명을 늘리는 도구가 아니라,
컴파일러에게 참조 관계를 설명하는 도구입니다.
정리
정리해보면 다음과 같습니다.
- Type Generic → 타입을 일반화
- Lifetime Generic → 참조 관계를 일반화
Lifetime은 성능과는 관련이 없습니다.
모두 컴파일 타임에 검사되며, 런타임 비용은 없습니다.
감사합니다.
'Rust' 카테고리의 다른 글
| [Rust] 실제 코드로 확인해보는 Monomorphization (0) | 2026.02.18 |
|---|---|
| [Rust] 패턴 바인딩 (Pattern Binding) (0) | 2026.02.17 |
| [Rust] unwrap 이해하기 (0) | 2026.02.14 |
| [Rust] 동시성 (0) | 2025.04.27 |
| [Rust] 컬렉션 (Collection) (0) | 2025.04.22 |