본문 바로가기
개발/러스트 (Rust)

Rust 기본 : 일반화 (Generics)

by 레조 2024. 9. 3.

https://rustacean.net/

 

Rust 기본 : 일반화 (Generics)

 

러스트(Rust)의 제네릭(Generics)은 다양한 데이터 타입에 대해 코드 사용 유연성을 높이고 코드 반복 구현을 줄여줍니다. 제네릭을 사용하면 컴파일 시에 구체적인 타입으로 대체될 제네릭 타입을 가진 구조체, 열거형, 함수를 정의할 수 있습니다.

 

1. 정의 (Define)

구조체 이름 뒤에 임의의 타입을 나타내는 <T>를 추가하여 선언한다.

struct Message<T> {
    id: String,
    payload: T,
}

 

2. 구현 (Implementation)

impl<T> : 구현부에서 제네릭 타입을 사용한다.

Message<T> : 구현 대상은 Message<T> 제네릭 타입이다.

impl<T> Message<T> {
    fn new(id: String, payload: T) -> Self {
        Message {
            id,
            payload,
        }
    }
    fn get_payload(&self) -> &T {
        &self.payload
    }
}

 

impl<T>를 사용하지 않으면 제네릭 구조체에 대한 구현이 아니기 때문에 구체적 타입을 제시해야한다.

(C++의 템플릿 특수화와 같다.)

impl Message<String> {
    fn print_payload(&self) {
        println!("{}", self.payload);
    }
}

 

또한 표준 라이브러리 enum 구현도 제네릭이다.

enum Option<T> {
    Some(T),
    None,
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}

 

마지막으로 일반 함수도 제네릭으로 구현 가능하다.

fn serialize_payload<T>(payload: T) -> String {
    "serialize payload".to_owned()
}

 

위에 구현한 내용들을 사용해보자.

struct Message<T> {
    id: String,
    payload: T,
}

impl<T> Message<T> {
    fn new(id: String, payload: T) -> Self {
        Message {
            id,
            payload,
        }
    }
    fn get_payload(&self) -> &T {
        &self.payload
    }
}

impl Message<String> {
    fn print_payload(&self) {
        println!("{}", self.payload);
    }
}

fn serialize_payload<T>(payload: T) -> String {
    // payload 처리후...
    "json".to_owned()
}

fn main() {
    let msg1 = Message {
        id: "Ping".to_owned(),
        payload: "from client".to_owned(),
    };
    let cmd2 = Message::new(
        "Pong".to_owned(),
        1
    );

    msg1.print_payload();
    //msg2.print_payload(); // 위에서 print 특수화 구현은 String만 하였다.

    let p1 = msg1.get_payload();
    let p2 = msg2.get_payload(); 

    serialize_payload(p1);
    serialize_payload(p2);
}