제너릭 타입
제너릭 타입은 자바에서의 것과 크게 다르지 않음.
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 |