제네릭과 트레이트

Generic


#![allow(unused)]
fn main() {
fn largest_i32(list: &[i32]) -> i32 {
    let mut largest = list[0];

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

    return largest;
}
}

→ 여기서 타입(i32)와 이름(largest_i32)만 바꾸면 다른 타입에서도 쓸 수 있지 않을까?

  • 타입 정보를 매개 변수로 전달하여, 개발자가 작성하는 코드의 반복을 줄이자.
  • 일반적으로, 타입 정보를 담고있는 변수를 T로 표시(Type)
  • 컴파일러에서 자동으로 각 타입에 해당되는 코드를 만들어서 그 것을 컴파일 한다.
  • 함수, 열거자, 구조체, 메서드에서도 사용할 수 있다.

제네릭을 적용한 함수


#![allow(unused)]
fn main() {
fn largest<T>(list: &[T]) -> T
{
    let mut largest = list[0];

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

    return largest;
}
}

제네릭을 이용한 구조체와 메서드


#![allow(unused)]
fn main() {
struct Point<T> {
    x: T,
    y : T,
}

impl<T> Point<T> { //제네릭으로 T 타입 매개변수를 받음을 명시해야 한다.fn
    fn x(&self) -> &T {
        return &self.x;
    }
}

impl Point<f32> { //특정 타입일 때만 적용되는 메서드
    fn distance_from_origin(&self) -> f32 {
        return (self.x.powi(2) + self.y.powi(2)).sqrt();
    }
}

}

특정 기능이 없는 타입이 제네릭에 들어가는 경우

  • T가 구현된 기능을 지원하지 않는 타입일 수도 있다.
  • 실제로도 largest 함수는 컴파일 에러가 발생한다.
   Compiling aggregator v0.1.0 (F:\Development\Rust Wing Study\rust-programming-language\ch8\aggregator)
error[E0369]: binary operation `>` cannot be applied to type `T`
  --> src\main.rs:56:17
   |
56 |         if item > largest {
   |            ---- ^ ------- T
   |            |
   |            T
   |
help: consider restricting type parameter `T`
   |

특정 기능이 구현되어 있는 지 어떻게 컴파일러에게 알려줄 것인가?

Trait

  • 여러 타입에서 할 수 있는 특정 기능을 추상적으로 정의하기

Trait 선언하기

트레이트 선언, 이름 , 그리고 타입의 행위를 정의할 메서드로 이루어짐


#![allow(unused)]
fn main() {
pub trait Summary {
    fn summarize(&self) -> String; //{} 를 붙이는 것이 아닌 ;을 붙인다.
}
}

#![allow(unused)]
fn main() {
pub trait Summary {
    fn summarize(&self) -> String {
        return String::from("대충 요약"); //미리 기본 구현을 할 수도 있다.
    }
}
}

Trait 구현하기

이미 선언된 트레이트를 가져와서 구현할 수도 있습니다. 단, 외부에서 정의된 타입에 외부에서 정의된 트레이트를 구현하는 건 불가능합니다. (같은 타입에 다른 트레이트가 같은 이름으로 구현될 수 있음.)


#![allow(unused)]
fn main() {
impl 트레이트_이름 for 타입이름 {
    fn 메서드_이름(&self) -> String {
        //세부 구현
    }
}
}

Trait와 Generic으로 함수 만들어 보기: 트레이트 매개변수

특정 트레이트를 구현한 타입만 제네릭의 인자로 받을 수가 있다. → 컴파일러한테 이 타입이 가능한 행위를 알려줄 수 있다.


#![allow(unused)]
fn main() {
fn notify(item: impl Summary) {}

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

fn notify<T: Summary + Clone>(item: T){}

fn notify<T>(item: T)
    where T: Summary + Clone
{}
}

트레이트를 구현하는 타입을 리턴하는 함수를 만들 때에도 트레이트 매개변수를 활용한다. 이 때 트레이트를 구현하는 타입은 한가지만 반환이 가능하다.


#![allow(unused)]
fn main() {
fn returns_trait(switch: bool) -> impl Summary {
    if switch {
        NewsArticle
    }
    else {
        Tweet
    }
}
}
   = note: to return `impl Trait`, all returned values must be of the same type