2023. 5. 11. 11:26ㆍLanguage/Rust
Rust를 공부하면서 Trait 패턴에 대해 처음 알게 되었습니다.
그래서 Trait이 Rust에서만 사용 되는 개념인가 했는데, 생각보다 여러 언어에서 채용하고, 또는 비슷한 개념으로 지원하고 있다는 것을 알게되었습니다.
PHP나 Scalar는 Trait이라고 해서 사용하는 거 같고, C#, Java, Javascript, Kotlin, Python의 경우는 Mixin이라는 개념으로 Trait과 비슷하게 지원하는거 같습니다. 그래서 자료를 찾을 때 Mixin이라고 찾으면 더 많이 나오고 이해하기 쉬운거 같습니다.
객체지향 프로그래밍 언어
위키백과에서 러스트를 찾아보면 순수 함수형 프로그래밍, 액터 기반 병렬 프로그래밍, 명령형 프로그래밍, 객체 지향 프로그래밍 스타일을 지원한다. 라고 나와있습니다.
우리가 객체지향 언어라고 말할 때는 상속, 캡슐화, 추상화, 다형성을 지원하는 언어를 말합니다.
하지만 Rust는 Class, Interface가 없습니다. 그래서 상속이라는 개념이 없기 때문에 완전한 객체지향 프로그래밍 언어라고 말하긴 힘들지만, 어느 정도 객체지향을 만들 수 있습니다.
그럼 어떻게 객체 지향을 지원할까요?? Rust에서는 Trait을 통해 객체지향을 지원할 수 있다고 나옵니다.
이제 Trait에 대해 알아보겠습니다.
트레잇(Trait)
일단 트레잇을 직역하면 특징, 특성 등으로 번역 됩니다.
Rust에서 말하는 Trait은 타입들이 공통적으로 갖는 동작에 대해 추상화하도록 해준다. Java의 Interface에서 추상화 함수를 정의함으로써 강제력을 제공하는 것과 유사하다.
Interface 처럼 함수를 정의만 해놓으면 이것을 받아 구현하는 쪽에서 강제로 구현하도록 하는 기능입니다.
이건 제 개인적인 생각이지만, Rust에서 Trait을 Interface 처럼 정의만 가능한게 아니라 구현도 가능하기 때문에 추상 클래스 같다는 느낌이 듭니다.
기존 클래스의 문제점
Rust에서는 왜 상속을 사용하지 않고 Trait이라는 개념을 도입 했을 까요??
클래스는 이제는 사실 조금 무겁습니다 - YouTube이 영상을 보시면 클래스의 여러 문제점들이 나옵니다.
예시를 들어보겠습니다.
우리가 객체지향을 이야기 할 때 많이 거론 되는 동물을 가지고 객체를 나눠 보겠습니다.
우리가 동물이라고 하면 움직이는 것을 동물이라고 하고 동물에는 걸어다니는 포유류, 날아다니는 새, 헤엄치는 물고기 등이 있습니다.
이걸 더 세부적인 객체로 나눠보겠습니다.
위 객체들을 잘 나눴나요??
개, 고양이, 사람은 포유류이면서 걸어다니고 상어와 붕어는 물고기이며 헤엄치기 때문에 잘 나눈거 같습니다.
하지만 먼가 이상하죠?? 독수리와 오리는 날아다닐 수 있지만, 걸어 다닐 수도 있습니다. 또 오리는 헤엄도 칠 수 있죠
객체를 변경해보겠습니다. 이젠 더 복잡해졌습니다.
돌고래는 포유류이지만 걸어다니지 못하고, 헤엄밖에 못 칩니다.
박쥐는 포유류이며, 걸어다니고 날아다닙니다.
날치는 물고기 이지만 날 수 있죠
이 특성들을 나눠 보면 아래 처럼 나눌 수 있습니다.
객체들의 공통 된 특성을 가지고 나누려고 하니 매우 복잡해지며, 나누기 애매한 객체들이 생기게 됩니다.
그래서 C#이나 Java에는 Decorator 패턴을 사용해서 이것을 해결하려고 시도합니다.
여기서 Trait이나 Mixin을 사용하면 좀 더 쉽게 객체의 특성을 나타 낼 수 있습니다.
특성과 객체를 따로 분리해보록 하겠습니다.
Trait을 쌓는다라는 용어를 많이 사용하는데, 보시면, 포유류, 새, 물고기는 동물이라는 특성을 포함하고 있습니다.
이렇게 Trait은 특성들을 조금씩 쌓아가면서 새로운 특성을 만들 수 있습니다.
이제 이 Trait을 사용해서 객체를 표현해보도록 하겠습니다.
상속이라는 개념이 없지만 모든 객체들을 특성을 통해 표현 할 수 있습니다.
박쥐는 포유류이며, 날 수있고, 날치는 물고기 이며, 헤엄치고 날 수 있습니다.
이걸 Rust 코드로 만들이 이렇게 나옵니다.
trait Animal {}
trait Mammal: Animal {}
trait Bird: Animal {}
trait Fish: Animal {}
trait Walker {
fn walk(&self) {
println!("I'm walking");
}
}
trait Swimmer {
fn swim(&self) {
println!("I'm swimming");
}
}
trait Flyer {
fn fly(&self) {
println!("I'm flying");
}
}
struct Dolphin;
impl Animal for Dolphin {}
impl Mammal for Dolphin {}
impl Swimmer for Dolphin {}
struct Bat;
impl Animal for Bat {}
impl Mammal for Bat {}
impl Walker for Bat {}
impl Flyer for Bat {}
struct Cat;
impl Animal for Cat {}
impl Mammal for Cat {}
impl Walker for Cat {}
struct Dove;
impl Animal for Dove {}
impl Bird for Dove {}
impl Walker for Dove {}
impl Flyer for Dove {}
struct Duck;
impl Animal for Duck {}
impl Bird for Duck {}
impl Walker for Duck {}
impl Swimmer for Duck {}
impl Flyer for Duck {}
struct Shark;
impl Animal for Shark {}
impl Fish for Shark {}
impl Swimmer for Shark {}
struct FlyingFish;
impl Animal for FlyingFish {}
impl Fish for FlyingFish {}
impl Swimmer for FlyingFish {}
impl Flyer for FlyingFish {}
fn main(){
let duck = Duck;
duck.swim();
let bat = Bat;
bat.walk();
bat.fly();
}
이 처럼 Rust는 코드 재사용을 위해 객체를 표현 할 때 상속이 아닌 특성을 통해 표현하게 됩니다.
'Language > Rust' 카테고리의 다른 글
Rust에서 RabbitMQ 사용하기 (WorkQueue) (0) | 2023.06.03 |
---|---|
if Let (1) | 2023.06.03 |
Rust 설치 및 VS Code 셋팅(Linux, Debian) (0) | 2023.04.26 |
Web Assembly 만들어 보기 (0) | 2023.04.15 |
Rust Option의 메서드 알아보기 (0) | 2023.04.01 |