Rust, 쉽게 하자!

Rust 입문

[Rust] Trait

바로크냥 2022. 10. 13. 21:15

1. Trait

트레이트는 다른 언어에서 인터페이스라고 불리는 기능과 비슷합니다. 

영어 단어 trait는 “특성, 특징”이라는 뜻을 가지고 있습니다.

특히 상속되는, 유전적인 특성을 의미합니다.

인터페이스의 역할은 인터페이스를 상속하는 클래스나 구조체에서 반드시 특정한 기능(함수)을 구현하도록 강제하는 것입니다. 

인터페이스에는 함수가 구현되어 있지 않고 시그니처만 있습니다. 즉 설계도만 있습니다.

트레이트도 마찬가지의 방법으로 사용합니다.

다른 점이 있다면 Rust의 트레이트에는  바디 부분을 미리 구현해 두고 사용할 수 있습니다. 

Rust에서 트레이트는 데이터 타입을 대상으로 구현됩니다.

데이터 타입에는 Rust가 기본적으로 제공하는 데이터 타입 외에 사용자가 만든 struct와 enum이 포함됩니다.

 

트레이트는 사용자가 직접 만들어 사용하는 것과 Rust가 미리 만들어서 기본 라이브러리로 제공하는 트레이트가 있습니다.

여기서는 직접 만들어 사용하는 트레이트를 다룹니다. 코드를 보면서 설명하겠습니다.

trait Calculus{      // 1 Calculus 트레이트 선언
    fn distance(&self)->f64;
}

struct Point{    // Point struct 선언
    x:f64,
    y:f64
}
impl Calculus for Point{    // 2 Point에 Calculus 구현
   fn distance(&self) -> f64 {
       let mut d = f64::sqrt(self.x * self.x + self.y * self.y);   // 3
        return d;
    }
}

fn main() {
   let point = Point{
       x:1.2,
       y:2.3
   };
    let mut dist = point.distance();
    println!("{:.2}", dist);
}

위 코드는 main 함수, Calcus trait, Point struct 그리고 Calculus trait의 distance 함수를 Point에 구현한 부분으로 이루어져 있습니다.

// 2: Calculus trait의 distance 함수를 구조체에 구현하는 부분은 

impl 트레이트 이름 for 구조체 이름{
           구현부
}

형식으로 되어 있습니다.

struct의 메서드를 구현할 때 사용했던 “impl” 키워드가 여기에서도 사용됩니다.

// 1: 트레이트를 사용하기 위해서는 우선 위의 예처럼 “trait” 키워드로 트레이트를 선언해야 합니다. 

trait 트레이트 이름{
  fn 구현 시킬 함수 이름(&self, 파라미터)-> 반환형;
}

트레이트 선언부 안에는 이 트레이트를 구현했을 때 반드시 구현해야 하는 함수의 시그니처가 정의됩니다.

함수의 몸체는 없습니다.

위의 코드는 Point라는 구조체에 Calculus 트레이트를 적용시켜서 distance라는 함수를 강제로 구현시키게 합니다. 

// 3: 구현된 distance 함수는 원점에서 점까지의 거리를 계산합니다.

f64::sqrt(self.x * self.x + self.y * self.y)

이 부분은 거리 계산을 구현한 코드입니다.

sqrt 메서드는 f64타입에 미리 정의되어 있는 것을 가져와서 사용했습니다.

 

트레이트가 다른 언어의 인터페이스와 비슷하다고 했지만, 사실 Rust의 트레이트는 특정한 데이터 타입에 어떤 기능이나 특성을 추가한다는 관점으로 보는 게 더 적절하지 않을까 합니다.

다음 코드는 이미 Rust에서 기본 데이터 타입으로 제공하는 String에 우리가 만든 트레이트를 구현한 코드입니다.

// 1 Show 트레이트 선언
trait Show {   
    fn show_upper(&self)-> String;
}
// 2 String 타입에 Show 트레이트 적용
impl Show for String{        
    fn show_upper(&self) -> String {
        self.to_uppercase()
    }
}

fn main() {
    let s = String::from("hello");
    let s1 = s.show_upper();
    println!("{} -> {}", s, s1);
}

// 출력
hello -> HELLO

// 1: Show라는 트레이트를 만들었습니다. 이 트레이트에는 show_upper 함수가 있습니다.

그래서 Show 트레이트를 사용할 때는 반드시 show_upper를 구현해야 합니다.

// 2: 이 부분이 구현 부분입니다. String 타입에 Show를 구현했습니다.

impl Show for String{
    fn show_upper(&self) -> String {
        self.to_uppercase()
    }
}

여기서 구현한 show_upper 함수는 단순하게 소문자를 대문자로 변환시켜서 String 타입으로 반환합니다.

당연한 이야기지만, 어떻게 구현할지는 사용자의 자유 입니다.

반드시 구현은 해야 하지만 어떻게 구현 할지는 정해진 게 없습니다. 사용자의 필요에 달려 있습니니다.

실행시켜 보면 이제 String 데이터 타입에는 show_upper 함수가 구현되어서 사용할 수 있습니다.

IDE에서는 그 상황에 사용할 수 있는 명령어 리스트를 자동으로 보여 줍니다.

다른 부분을 다 작성한 후 main 함수의 

let s1 = s.show_upper();

부분을 코딩할 때 s 이후에 “.”을 입력하면 곧바로 사용 가능한 명령어 리스트가 뜨는데 여기에 s.show_upper 함수가 들어 있습니다.

아래는 pycharm을 제작한 jetBrain 사의 Clion에서 캡처한 사진입니다.

 이 이미지를 보면 show_upper가 String 타입의 메서드로 등록되어 있는 것을 볼 수 있습니다.

 

 
 
 
 
반응형

'Rust 입문' 카테고리의 다른 글

[Rust] Dynamic Object  (0) 2022.10.13
[Rust] 트레이트 바운드 (Trait Bound)  (0) 2022.10.13
[Rust] Generic  (0) 2022.10.12
[Rust] use 사용하기  (0) 2022.10.11
[Rust] super  (1) 2022.10.11