본문 바로가기

Rust

[Rust] Generic Lifetime

 

안녕하세요.

오늘은 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 규칙이 있기 때문입니다.

대표적인 규칙은 다음과 같습니다.

  1. 각 입력 참조는 고유한 lifetime을 가진다.
  2. 입력이 하나라면, 출력은 그 lifetime을 따른다.
  3. 메서드에서 &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