Rust, 쉽게 하자!

Rust 입문

[Rust] Dynamic Object

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

3. Dynamic Object

Dynamic Object는 트레이트를 구조체 같은 데이터 타입처럼 사용할 수 있게 하는 기능입니다.

Dynamic Object는 앞 장에서 살펴본 트레이트 바운드와 비교해서 살펴봐야 합니다. 둘 다 제네릭에 트레이트를 구현할 때 아주 중요한 역할을 하기 때문입니다. (엄밀히 말하면 Dynamic Object는 제네릭과 상관없습니다. 다만 그 결과가 트레이트 바운드된 제네릭과 비슷합니다.)

 

트레이트 바운드를 사용한 <T: Copy>은  “어떤 데이터 타입 T가 있는데 T는 Copy 트레이트를 구현하고 있다, 또는 Copy 특성을 가지고 있다.”라고 선언하는 형식입니다.

Dynamic Object는 “dyn” 키워드를 사용해서 <dyn Copy>처럼 사용합니다. “Copy 트레이트를 가진 어떤 데이터 타입을 사용하겠다.”라고 알리는 느낌입니다. 

전자는 정적이고 후자는 동적이라고 말합니다. 그래서 후자의 명칭도 Dynamic Object입니다.

 

우리가 제네릭을 사용하더라도 컴파일러는 언젠가는 명확한 타입을 결정해야 합니다. 

정적으로 트레이트 바운드를 사용하면 컴파일할 때 제네릭의 실제적 타입이 결정됩니다.

Dynamic Object를 사용하면 런타임에 결정됩니다. 

또 정적인 트레이트 바운드로 구현하면 동적으로 Dynamic Object로 구현했을 때 보다 실행 파일의 크기가 좀 더 크지만 속도 면에서 이익을 본다고 합니다. 

예제를 보겠습니다.

trait Shape {         // 1
    fn show_me(&self);
}

struct Line;      // 2

impl Shape for Line {
    fn show_me(&self) {
        println!("Line...");
    }
}

struct Circle;

impl Shape for Circle {
    fn show_me(&self) {
        println!("Circle...");
    }
}

struct Rectangle;

impl Shape for Rectangle {
    fn show_me(&self) {
        println!("Rectangle...");
    }
}

struct Shapes {      // 3
    shape: Box<dyn Shape>,
}

impl Shapes {       // 4
    fn new() -> Shapes {
        Shapes {
            shape: Box::new(Line {})
        }
    }
    fn change_shape(&mut self, s: Box<dyn Shape>) {
        self.shape = s;
    }
    fn show(&self) {
        self.shape.show_me();
    }
}

fn main() {
    let mut shape1 = Shapes::new();
    shape1.show();
    shape1.change_shape(Box::new(Circle {}));
    shape1.show();
    shape1.change_shape(Box::new(Rectangle {}));
    shape1.show();
}

// 출력
Line...
Circle...
Rectangle...

// 1: 트레이트 Shape을 선언했습니다.

Shape 트레이트를 사용하면 show_me 메서드를 구현해야 합니다.

// 2: Line, Circle, Rectangle 등의 구조체가 선언되었습니다.

그리고 각각 Shape 트레이트도 구현했습니다.

// 3: Shapes 구조체에는  shape 필드가 있습니다. 

shape는 Box <dyn Shape> 타입입니다.

의미를 풀어 보자면 “shape 필드는 Shape 트레이트를 구현한 어떤 타입의 객체를 담은 Box 타입이다.” 정도가 되겠습니다.

여기서 Box에 넣지 않고 그냥 “dyn Shape” 타입이라고 선언하면 에러가 발생합니다.

Box는 스마트 포인터를 다룰 때 살펴보겠습니다.

Box는 대표적이고 가장 기본적인 스마트 포인터인데 heap 메모리 영역에 데이터를 저장하고 어떤 정도의 크기인지 정확하게 결정하기 어려운 대상을 담아주면 다루기 수월하게 됩니다.

여기서도 <dyn Shape>이 어떤 타입인지 정확하게 알 수가 없기 때문에 그 크기를 알 수가 없습니다.

그래서 Box에 넣어서 다루고 있습니다. 

// 4: Shapes 구조체의 메서드는 Shape> 타입의 shape 필드를 초기화하고 바꾸고 출력합니다.

 

 

 
반응형

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

[Rust] 알아 두면 도움되는 standard trait (1/2)  (2) 2022.10.13
[Rust] Standard trait와 derivable trait  (0) 2022.10.13
[Rust] 트레이트 바운드 (Trait Bound)  (0) 2022.10.13
[Rust] Trait  (0) 2022.10.13
[Rust] Generic  (0) 2022.10.12