8. 트레이트 이해와 Rust에서 trait의 역할
러스트의 트레이트는 다른 언어에서의 인터페이스와 유사한 공유 동작을 정의하는 강력한 메커니즘을 제공합니다. 트레이트의 세계로 들어가보고 러스트 프로그래밍에서의 중요성을 이해해봅시다.
트레이트 정의
우리가 여우를 나타내는 RedFox
구조체가 있다고 상상해봅시다. 여우들이 소리를 내는 것과 같은 공통 동작을 가지길 원합니다. 우리는 Noisy
라는 트레이트를 정의할 수 있습니다. 이 트레이트는 해당 트레이트를 구현하는 모든 구조체가 get_noise()
메서드를 가져야 한다고 명시합니다. 이 메서드는 String slice reference(&str)
을 반환해야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
trait Noisy {
fn get_noise(&self) -> &str;
}
struct RedFox {
name: String,
}
impl Noisy for RedFox {
fn get_noise(&self) -> &str {
"meow"
}
}
트레이트 활용
트레이트의 강력함은 트레이트를 구현하는 모든 타입을 받아들이는 제네릭 함수를 작성할 때 드러납니다. 예를 들어, Noisy
를 구현한 모든 타입 T
를 인자로 받는 print_noise()
함수를 고려해봅시다. 이 함수는 입력 매개변수 item
에서 Noisy
트레이트에 정의된 동작을 사용할 수 있습니다.
1
2
3
4
fn print_noise<T: Noisy>(item: T) {
println!("{}", item.get_noise());
}
다음은 Human 과 Robot struct를 구현한 후 run method를 멤버함수로 갖는 trait을 구현하고, 제네릭 함수를 사용해 Run trait를 구현한 오브젝트를 호출하는 예제 코드 입니다. 특히 Run trait에 default method를 두어 Run trait을 상속한 Structure가 run method를 구현하지 않았을 때 default method가 호출하게 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
trait Run {
fn run(&self) {
println!("I'm running");
}
}
struct Robot {}
impl Robot {
pub fn new() -> Self {
Self {}
}
}
impl Run for Robot {
fn run(&self) {
println!("로봇 뛴다");
}
}
struct Human {}
impl Human {
pub fn new() -> Self {
Self {}
}
}
impl Run for Human {}
fn run<T: Run>(object: T) {
object.run();
}
fn main() {
let robot = Robot::new();
let human = Human::new();
run(robot); // 로봇 뛴다
run(human); // I'm running
}
특별한 트레이트: Copy
Copy
와 같은 특별한 트레이트는 특수한 기능을 제공합니다. *Copy
를 구현하는 타입은 이동하는 대신 복사됩니다. 이는 정수와 같은 작은 스택 기반 타입에 유용합니다. 이는 원래 값이 그대로 유지되도록 보장합니다.
Copy 트레이트는 스택에 저장되는 타입의 값을 복사할 때 사용됩니다. 따라서 Copy 트레이트를 구현하는 타입은 이동(move) 대신 복사(copy)됩니다. 여기에 간단한 예제 코드가 있습니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#[derive(Debug, Copy)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // 이동(move)이 아닌 복사(copy)가 발생합니다.
println!("p1: {:?}", p1); // p1은 여전히 사용 가능합니다.
println!("p2: {:?}", p2);
}
이 예제에서는 Point
구조체를 정의하고, Copy
와 Clone
트레이트를 함께 구현합니다. 그런 다음 main
함수에서 p1
을 p2
에 할당할 때 이동 대신 복사가 발생합니다. 따라서 p1
은 여전히 사용 가능합니다.
트레이트 계층 구조
트레이트는 상속을 지원하여 트레이트 계층 구조를 만들 수 있습니다. 트레이트는 다른 트레이트를 상속받아 동작을 구조적으로 조직하는 데 사용됩니다. 이 접근 방식은 복잡한 관계를 간단하게 표현하고 코드의 가독성과 유지 관리성을 향상시킵니다.
기본 동작
트레이트는 메서드에 대한 기본 동작을 정의할 수 있습니다. 트레이트를 구현할 때 메서드의 구현이 제공되지 않으면 트레이트에서 정의된 기본 동작이 사용됩니다. 이 기능은 코드 재사용을 촉진하고 중복을 줄입니다.
제한과 해결 방안
트레이트는 직접 필드를 정의할 수 없습니다. 이를 해결하기 위해 트레이트 정의에서 setter 및 getter 메서드를 사용합니다.
결론적으로 트레이트를 활용하면 러스트 프로그래밍에서 공유 동작을 캡슐화하고, 코드 재사용성을 증가시키며, 견고하고 유지 관리 가능한 응용 프로그램을 구축할 수 있습니다.