제네릭

제네릭 (Generic)

제네릭 타입

함수, 메서드, struct, enum 등을 정의할 때 일반적으로 구체적인 데이타 타입을 지정하게 되는데, 이렇게 하는 대신 임의의 타입을 받아들일 수 있게 만든 것을 제네릭 (Generic)이라 한다. 제네릭은 함수, 메서드, struct, enum 등을 정의할 때 <T> 과 같은 타입 파라미터를 지정하여 정의하는데, 이때의 T에는 여러 가지 타입들(예를 들어, i32, f64, String 등)이 들어갈 수 있다. 제네릭의 타입 파라미터는 필요에 따라 하나 이상 여러 개를 지정할 수도 있다. 제네릭을 사용하면 모든 타입마다 중복된 코드를 작성하지 않아도 되고, 코드를 더 유연하게 사용할 수 있게 된다.

제네릭으로 간단한 struct를 만들어 보자. 아래는 Point 라는 구조체로서 2개의 점을 가진 제네릭 타입이다. 아래 예제에서 pt1 은 정수(i32)를 받아들이고 있으므로 컴파일러는 구조체를 Point<i32> 으로 사용하고, pt2 는 부동소수점 숫자를 받아 들이므로 Point<f64> 를 사용할 것이다.

struct Point<T> {
    x: T,
    y: T
}

fn main() {
    let pt1 = Point { x: 1, y: 1 };
    let pt2 = Point { x: 2.0, y: 2.0 };
}

제네릭의 다른 예로, 아래 Result 열거형 타입은 표준 라이브러리에 들어 있는 타입으로 이 타입은 2개의 타입 파라미터(T, E)를 사용하는 열거형 제네릭 타입이다. Result 열거형에서 Ok(T) variant 에서는 T 라는 타입 파라미터를 사용하고, Err(E) 에서는 E 라는 타입 파라미터를 사용하고 있다.

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

아래는 위의 Result 제네릭을 사용하는 예로서, 함수는 Result<String, io::Error> 타입을 리턴하고 있다. 즉 Ok variant의 경우는 String 타입의 데이타가 사용되고, Err variant의 경우는 std::io::Error 타입이 사용된다.

fn func1() -> Result<String, io::Error> {
    //... 생략...
}

구조체 제네릭에 대한 메서드를 구현하기 위해서는, 아래 예제와 같이 impl<T> 와 같이 impl 뒤에 제네릭 타입 파라미터 T를 지정하고, 구조체(여기서는 Point<T>)에 대한 메서드를 추가하면 된다. 메서드는 함수와 마찬가지로, 타입 파라미터 T를 입력 파라미터 혹은 리턴 타입에 사용할 수 있다.

struct Point<T> {
    x: T,
    y: T
}

impl<T> Point<T> {
    fn get_x(&self) -> &T {
        &self.x
    }
}

fn main() {
    let p = Point { x: 2.0, y: 2.0 };
    println!("{}", p.get_x());    
}

Rust에서 제네릭은 컴파일 타임에 구체적인 타입(concrete type)으로 대체되기 때문에, 런타임 성능에 어떤 영향도 미치지 않는다. 즉, 컴파일 타임에 이미 제네릭 타입을 구체적인 타입으로 대체하여 코드를 수행한다.

This site is not affiliated with or endorsed by the Rust Foundation or Rust Project.