본문 바로가기
개발/러스트 (Rust)

Rust 기본 : 상위 특성 (Supertraits)

by 레조 2024. 9. 19.

https://rustacean.net/

 

Rust 기본 : 상위 특성 (Supertraits)

 

Rust는 데이터와 행위를 분리하도록 설계되어 있습니다. 

데이터는 구조체(struct)와 열거형(enum)으로 정의하고,

행위는 트레잇(trait)을 통해 정의합니다.

(유연성과 안전성 제공)


Supertraits를 사용하는 것은 트레잇간의 관계를 정의하고, 

트레잇을 구현할 때 필요한 기본 기능을 강제하는 방법입니다. 

(코드의 명확성과 유지보수성을 향상)

 

결국 Supertraits은 기능 확장의 순서를 잡아줍니다.

B가 A를 supertrait으로 가지면, B를 구현하기 전에 A를 구현해야 합니다.

 

의존이냐 확장이냐.

 - B가 A의 기능에 의존

 - A의 기능을 B로 확장

trait A {
    fn do_something_a(&self);
}

// B:A - B 트레잇을 사용하려면 A 트레잇을 반드시 구현해야한다.
trait B: A {
    fn do_something_b(&self);
}

struct SomeType;

impl A for SomeType {
    fn do_something_a(&self) {
        println!("Performing something A");
    }
}

impl B for SomeType {
    fn do_something_b(&self) {
        // A의 메소드 호출 가능
        self.do_something_a();
        println!("Performing something B");
    }
}

fn main() {
    let my_obj = SomeType;
    my_obj.do_something_b();
}

 

supertrait을 사용하여 trait bound를 단순화 할 수 있습니다.

그러나 복잡한 트레잇 관계는 코드 직관성을 떨어뜨립니다.

범주 특징 상세
장점 중복 감소 상위 트레잇에 공통 기능을 정의하여 하위 트레잇 구현의 중복성을 줄인다.
  구현 강제화 특정 트레잇이 다른 트레잇의 기능을 강제로 요구한다.
단점 코드 은닉성 복잡한 트레잇 구조는 코드 가독성을 떨어뜨린다.
  유연성 감소 구현 강제화가 주는 부작이다.
균형 찾기 문서화 각 트레잇과 그 관계를 명확히 문서화 한다.
  단순성 유지 트레잇 관계를 간단히 하여 그 역할과 책임을 분명히 한다.

 


 

전체 코드

trait Animal {
    fn make_sound(&self);
    fn given_name(&self) -> String;
}

trait Playful: Animal {
    fn play(&self) {
        println!("Animal is sleeping...");
    }
}

/* -------------------------------------------------------------------------- */

struct Dog {
    name: String,
}

// 메소드 구현
impl Dog {
    fn sleep(&self) {
        println!("The dog, {}, is sleeping.", self.given_name());
    }
}

// 트레잇 구현
impl Animal for Dog {
    fn make_sound(&self) {
        println!("{} says woof!", self.given_name());
    }

    fn given_name(&self) -> String {
        self.name.clone()
    }
}

// 기본 트레잇 덮어쓰기 (trait overwrite)
impl Playful for Dog {
    fn play(&self) {
        println!("{} loves playing fetch with a ball!", self.given_name());
    }
}

/* -------------------------------------------------------------------------- */

// supertrait 관계이므로 Animal 트레잇은 생략할 수 있다. (Playful + Animal)
fn dye_animal_red<T>(object: &T) where T: Playful {
    println!("{} animal dye red.", object.given_name());
    object.play();
}

/* -------------------------------------------------------------------------- */

fn main() {
    let dog = Dog { name: "Buddy".to_string() };
    dye_animal_red(&dog);
}

 

결과

Buddy animal dye red.
Buddy loves playing fetch with a ball!