본문 바로가기

Rust

트레이트, 라이프타임

트레이트(trait)

다른 언어에서의 추상 클래스 역할을 rust에서는 trait가 담당한다.

 

trait는 함수의 인자와 반환에도 활용 가능하다.

외부 타입에 외부 트레이트를 구현하는건 불가능하다.

트레이트나 타입 둘 중 하나는 내 크레이트에 존재해야 한다.

기본 구현을 적어둘 수 있다.

 

트레이트 바운드 라는 것이 있다.

트레이트 바운드란 위에 얘기한 함수의 인자와 반환에 활용하는 것으로 특정 trait를 구현한 타입만을 사용하도록 명시하는 것이다.

 

trait는 다른 언어의 추상 클래스와 다르게 상속의 개념이 아닌 조립의 개념을 사용한다.

그래서 impl Display + (my trait) 이런 형태로 여러 트레이트를 구현한 타입을 인자로 받거나 반환하는 트레이트 바운드가 가능하다.

또한 필드를 가지지 못한다. 상수, 함수 만 내부적으로 가지는게 가능하고 데이터를 저장하는 것은 불가능하다.

 

트레이트가 복잡하면 where 키워드로 가독성 좋게 변환 가능하다.

 

포괄 구현(blanket implementations)을 지원한다.

A 트레이트를 구현하는 모든 타입에 대하여 다른 B 트레이트를 구현시켜야 할 때 손쉽게 구현을 보장할 수 있다. 

아래 코드에 대한 설명 - Display 트레이트를 구현하는 모든 타입(T)에 대해 ToString 트레이트를 구현한다

 

trait 사용 예시

pub trait Summary {
    fn summarize(&self) -> String;
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {}", self.headline, self.author)
    }
}

pub fn notify(item: &impl Summary) { ... }

pub fn notify<T: Summary>(item: &T) { ... }

//use where
fn some_function<T, U>(t: &T, u: &U) -> i32
where T: Display + Clone,
      U: Clone + Debug
{ ... }

//blanket implementations
impl<T: Display> ToString for T {
    // -- 생략 --
}

 

 

라이프 타임(life time)

rust의 경우 댕글링 포인터에 대하여 컴파일 타임에 방지한다.

이를 위해 rust는 라이프타임을 명시적으로 작성해야 하는 경우가 발생한다.

 

rust의 경우 스코프 단위로 변수의 생명을 체크한다.

 

함수에 인자를 참조로 넘기고 참조로 받는 경우에 대하여 특정 인자의 라이프 타임이 적은 경우 함수 호출부에서 댕글링 포인터의 발생 여지가 생긴다.

 

이를 rust는 함수 구현시에 라이프타임에 대한 명시를 강제하여 다른 라이프타임을 가지는 인자들이 들어와도 최소 라이프 타임을 기준으로 반환값의 라이프타임을 결정한다.

 

구조체의 필드 값중에서 참조 변수가 존재하는 경우에도 라이프타임을 명시해야 한다.

 

라이프타임을 명시하지 않아도 컴파일이 되기 위해서는 3가지 규칙을 통과해야 한다.

1. 입력 매개변수의 개별 할당 (Input Rule)

함수의 매개변수 중 참조자가 있다면, 컴파일러는 일단 모든 참조자에 서로 다른 라이프타임을 붙여준다.

2. 단일 입력의 수명 전이 (Output Rule)

만약 입력 참조자 매개변수가 딱 하나라면, 컴파일러는 그 수명을 모든 출력 참조자에 그대로 복사해서 붙여준다

3. 메서드에서의 self 우선 (Method Rule)

구조체의 메서드(impl 블록 안의 함수)인 경우, 매개변수가 여러 개 있더라도 그중 하나가 &self 또는 &mut self라면, 반환값의 라이프타임은 무조건 self의 수명을 따라간다.

 

'static 라이프타임 키워드를 사용하면 다른 언어에서 처럼 프로그램이 동작하는 동안 계속 살아있게 된다.

//case 1
fn main() {
    let string1 = String::from("long string is long");

    {
        let string2 = String::from("xyz");
        let result = longest(string1.as_str(), string2.as_str());
        println!("The longest string is {}", result);
    }
}

//case 2
fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
    }
    println!("The longest string is {}", result);
}

//라이프타임 명시없는 경우 컴파일 에러 발생
//라이프타임 명시가 없는 경우 컴파일러가 어떤 변수의 라이프타임으로 반환값의 라이프타임을 정할지 모름
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    let i = ImportantExcerpt {
        part: first_sentence,
    };
}

'Rust' 카테고리의 다른 글

모듈시스템  (0) 2026.01.08
열거형 (enumerate)  (0) 2026.01.06
구조체 (struct), 약간의 메크로 얘기  (0) 2026.01.05
소유권 (Ownership)  (0) 2026.01.04
조건, 반복문  (0) 2026.01.04