제너릭 타입



제너릭 타입은 자바에서의 것과 크게 다르지 않음.


fn largest_i32(list: &[i32]) -> i32 {
let mut largest = list[0];

for &item in list.iter() {
if item > largest {
largest =item;
}
}
largest
}

fn largest_char(list: &[char]) -> char {
let mut largest = list[0];

for &item in list.iter() {
if item > largest {
largest =item;
}
}
largest
}


위 두 함수를 제너릭으로 바꾸자면,


fn largest<T>(list: &[T]) -> T {


이렇게 쓰면 됨.


그런데 위와 같이 바꾸고 컴파일 시도하면 에러가 남. 이유는 타입 T 가 어떻게 값을 비교해야 하는지 정의되지 않았기 때문에 item > largest 코드가 에러를 일으킨 것. 이에 관하여는 후에 자세히 다룸.



구조체에 제너릭 타입 사용


특별할 것 없음.


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

struct AnotherPoint<T, U> {
x: T,
y: U,
}



메서드에 제너릭 타입 사용


아래와 같이 사용.


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

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


아래는 조금 다름.


impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}


위의 경우, Point<T> 에서도 Point<f32> 타입에만 메서드를 구현한 것. Point<i32> 인스턴스를 생성한다면 그 인스턴스는 위의 메서드를 사용할 수 없음. 아래와 같이 일부 제너릭 타입만 한정하는 것도 가능.


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

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


또, 아래와 같이 메서드에 다른 제너릭 타입을 부여하여 섞어 사용하는 것도 가능(사실 이건 자바에서도 가능..).


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

impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}



제너릭의 성능 문제?


러스트의 제너릭은 런타임 비용이 없음. 컴파일 타임에 코드의 monorphization(단일 형태화) 을 수행함. 이는 제너릭 코드를 구체적인 타입 코드로 변환하는 일. 컴파일러는 사용자가 제너릭 코드를 작성하는 과정과 반대의 절차를 거쳐서 구체적인 코드를 생성해냄.


아래와 같은 코드가 있다면,


let integer = Some(5);
let float = Some(5.0);


컴파일러는 다음과 같은 코드를 만들어냄



enum Option_i32 {
Some(i32),
None,
}

enum Option_f64 {
Some(f64),
None,
}

fn main() {
let integer = Option_i32::Some(5);
let float = Option_f64::Some(5.0);
}


이런 과정을 컴파일 타임에 모두 거치기 때문에 제너릭 타입에 런타임 시 타입 추론 등의 동작이 아예 발생하지 않음. 때문에 성능상의 비용은 없음.

'Rust' 카테고리의 다른 글

라이프타임  (0) 2018.02.28
트레잇(Traits)  (0) 2018.02.28
에러 핸들링(Result)  (0) 2018.02.26
에러 핸들링(panic)  (0) 2018.02.26
컬렉션(해시맵)  (0) 2018.02.25

+ Recent posts