11장: 제네릭과 트레이트
Preview
- 러스트에서는 제네릭과 트레이트를 통해 다형성을 지원함.
- 러스트의 다형성 구현은 하스켈의 타입클래스를 벤치마킹하여 만들어짐.
개념 톺아보기
trait
- 여러 타입이 공통적으로 가지는 동작(공통으로 구현해야 하는 메서드의 집합)을 정의. → 인터페이스와 유사
- 예를 들어, "걸음"라는 트레이트를 정의하고 그 안에는 “걷기”, “뛰기”, “멈추기” 메서드가 존재함 → 사람과 로봇이 이 트레이트를 구현한다면 사람과 로봇 모두 "걸음”라는 트레이트 가진 것.
- 트레이트를 구현한 타입은 해당 트레이트가 제공하는 메서드를 사용할 수 있음
// 예시: std::io::Write
trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize>;
fn flush(&mut self) -> Result<()>;
fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... }
... }
- 함수정의
say_hello
함수는 Write
트레이트를 구현하는 어떤 타입의 가변 참조라도 받아들일 수 있다.
- 동적 디스패치로 실행 시에 들어오는 데이터 타입에 따라 작동한다.
use std::io::Write;
fn say_hello(out: &mut dyn Write) -> std::io::Result<()> {
out.write_all(b"hello world\\n")?;
out.flush()
}
- 파일에 쓰는 예제
- File 데이터 타입은
Write
트레이트가 구현되어 있다. → 사용 가능
- fs.rs 코드
hello.txt
File 데이터 타입의 변수 생성 → Write
제약조건 만족 → 정상작동
use std::fs::File;
let mut local_file = File::create("hello.txt")?;
say_hello(&mut local_file)?; // works
- 메모리 버퍼에 올리는 예제
Vec<u8>
데이터 타입 또한 Write
트레이트가 구현되어 있다 → 사용 가능
let mut bytes = vec![];
say_hello(&mut bytes)?; // also works
assert_eq!(bytes, b"hello world\\n");
trait object: dyn
- 서로 다른 데이터 타입이지만 같은 trait을 구현하는 다양한 객체들을 동일한 방식으로 다룰 수 있게 해주는 구현 방법
- 런타임 시점에 크기가 결정되기 때문에 반드시 참조(
&
또는 &mut
)나 스마트 포인터(Box
, Rc
, Arc
등)로 사용되어야 함 → 참조로 받으면 컴파일 시 포인터 값만 들어가서 길이가 고정되기 때문에 가능