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

Rust 기본 : 특성 (Traits)

by snowoods 2024. 9. 7.

https://rustacean.net/

 

Rust 기본 : 특성 (Traits)

 

러스트는 상속을 지원하지 않습니다.

대신 함수를 공유하고 인터페이스를 제공하기 위해 트레잇(trait)를 사용합니다.

이는 다른 언어의 인터페이스와 유사합니다.

 

트레잇은 타입의 행동을 추상화하고 규정하는 데 사용합니다.

이를 통해 코드 유연성, 재사용성, 모듈성을 높일 수 있습니다.

 

트레잇(trait) 선언하기.

1. 공통 특징 트레잇 'Animal' 정의.

동물이 공통적으로 가질 수 있는 특징을 트레잇으로 정의합니다.

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

 

2. 행동 중심 트레잇 'Playful' 정의.

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

 

3. 대상 객체 정의.

struct Cat {
    name: String,
}

struct Dog {
    name: String,
}

 

트레잇(trait) 구현하기.

2. Cat 트레잇 구현.

// 선언만 한 트레잇은 반드시 구현해야한다.
impl Animal for Cat {
    fn make_sound(&self) {
        println!("{} says meow!", self.given_name());
    }

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

// 트레잇이 기본 구현을 가지고 있다면 추가 구현 없이 객체에 바로 적용할 수 있다.
impl Playful for Cat {}

 

3. Dog 트레잇 구현.

// 메소드 구현
impl Dog {
    fn sleep(&self) {
        println!("The dog, {}, is sleeping.", self.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());
    }
}

 

4. 코드로 확인해보자.

fn main() {
    let dog = Dog { name: "Buddy".to_string() };
    let cat = Cat { name: "Whiskers".to_string() };

    dog.make_sound();
    cat.make_sound();

    dog.play();
    dog.sleep();    
    cat.play();
}

 


 

전체 코드

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

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

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

struct Cat {
    name: String,
}

// 선언만 한 트레잇은 반드시 구현해야한다.
impl Animal for Cat {
    fn make_sound(&self) {
        println!("{} says meow!", self.given_name());
    }

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

// 트레잇이 기본 구현을 가지고 있다면 추가 구현 없이 객체에 바로 적용할 수 있다.
impl Playful for Cat {}

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

struct Dog {
    name: String,
}

// 메소드 구현
impl Dog {
    fn sleep(&self) {
        println!("The dog, {}, is sleeping.", self.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());
    }
}

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

fn main() {
    let dog = Dog { name: "Buddy".to_string() };
    let cat = Cat { name: "Whiskers".to_string() };

    dog.make_sound();
    cat.make_sound();

    dog.play();
    dog.sleep();
    cat.play();
}

 

5. 결과

Buddy says woof!
Whiskers says meow!
Buddy loves playing fetch with a ball!
The dog, Buddy, is sleeping.
Animal is sleeping...

 

 

참고

트레잇 만들기

 

 - 공통 특성 찾기 : 객체간의 공통 행동이나 속성을 기준으로 만든다.

   ex) 움직이다, 먹다, 털색 같은 경우 Animal 트레잇으로 포괄할 수 있다.

 

 - 인터페이스 분리 원칙 (Interface Segregation Principle) : 객체 지향 설계 원칙 SOLID 중 하나.

   객체는 자신이 사용하지 않는 메소드에 의존하지 않아야한다.

   즉, 구조체 구현에서 trait으로 분리하여 관리한다.

 

 - 행동 기반 설계 : 객체의 상태 보다 행동에 초점을 맞춰 트레잇 정의.

   객체가 무엇을 할 수 있는가에 따라 인터페이스 설계. 다형성 통합에 도움.

 

 - 재사용성 증대

   재사용 가능한 코드를 만들기 위한 방법인데 공통 특성 찾기와 닮아있다.

   ex) 공통 기능 : 시리얼라이징, 비교, 출력 등

 

 - 제네릭 타입의 제약 조건

   제네릭 타입이 특정 행동을 보장하며 코드 안정성과 명확성을 향상합니다.

 

행동 기반 설계

trait Drawable {
    fn draw(&self);
}

trait Updateable {
    fn update(&mut self);
}

struct Player {
    position: (i32, i32),
}

impl Drawable for Player {
    fn draw(&self) {
        println!("Player at position {:?}", self.position);
    }
}

impl Updateable for Player {
    fn update(&mut self) {
        // Update player's position
        self.position.0 += 1;
    }
}