Updated trait-objects.md

jade.kim authored
revision 809e8554c09b45f133b14cda74caf06a4b8048f6
trait-objects
# 5.23. 트레잇 객체 (Trait Objects)

다형성과 관련된 코드가 실행될 때, 어떤 버전의 코드가 실행될지 결정하는 메카니즘이 필요합니다.
이것을 'dispatch' 라고 하며, 두 가지의 주요 형태가 있습니다: 정적 dispatch 와 동적 dispatch.
rust 는 정적 dispatch 를 선호하는데, 'trait objects' 라는 메커니즘을 통해서 동적 dispatch 역시 지원합니다.

## 배경

이 장에서 트레잇과 몇 개의 구현체가 필요합니다.
`Foo` 라는 간단한 코드를 만들어 봅시다. `String` 을 리턴하는 하나의 메소드를 갖고 있습니다.

```rust
trait Foo {
fn method(&self) -> String;
}
```

`u8`과 `String` 에 대해 이 트레잇을 구현 할 것입니다.

```rust
# trait Foo { fn method(&self) -> String; }
impl Foo for u8 {
fn method(&self) -> String { format!("u8: {}", *self) }
}

impl Foo for String {
fn method(&self) -> String { format!("string: {}", *self) }
}
```

## 정적 dispatch

정적 dispatch 를 수행하기 위해, 이 트레잇과 트레잇 바운드를 사용할 수 있습니다:

```rust
# trait Foo { fn method(&self) -> String; }
# impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } }
# impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } }
fn do_something(x: T) {
x.method();
}

fn main() {
let x = 5u8;
let y = "Hello".to_string();

do_something(x);
do_something(y);
}
```

여기서 rust 는 정적 dispatch 를 수행하기 위해 단형화(monomorphization)를 사용합니다.
이 말은 rust 는 `u8` 과 `String` 을 위한 특별한 버전의 `do_something()`을 만들고, 이 메소드에 대한 호출부들을 이 구체화된 함수들에 대한 호출로 바꾼다는 것을 의미 합니다. 다르게 말하면, rust 는 이렇게 생성 합니다:

```rust
# trait Foo { fn method(&self) -> String; }
# impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } }
# impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } }
fn do_something_u8(x: u8) {
x.method();
}

fn do_something_string(x: String) {
x.method();
}

fn main() {
let x = 5u8;
let y = "Hello".to_string();

do_something_u8(x);
do_something_string(y);
}
```

이것은 아주 큰 장점을 지니는데: 함수 호출하는 부분을 컴파일 타임에 알 수 있기 때문에, 정적 dispatch 는 함수 호출을 인라인 할 수 있으며, 인라인은 좋은 최적화의 핵심 입니다. 정적 dispatch 는 빠르지만 단점이 있습니다: 바이너리에 각 타입 별로 같은 함수의 복사본들이 있기 때문에 코드가 커집니다. (code bloat)

뿐만 아니라, 컴파일러는 완벽하지 않기 때문에 "최적화" 코드가 더 느려질 수 도 있습니다.
예를 들면, 너무 과하게 함수가 인라인되면 명령어 캐시를 넘칠 수 있습니다. (캐시가 우리 주변의 모든 것을 지배합니다)
이것이 `#[inline]`과 `#[inline(always)]` 이 신중하게 사용되어야 하는 이유이고, 동적 dispatch 가 가끔 더 효율적인 이유 입니다.

그러나, 일반적인 경우 정적 dispatch 를 사용하는 것이 더 효율적이고, 항상 정적으로 dispatch 되는 wrapper 함수가 동적 dispatch 를 수행하도록 할 수 있습니다. 반대로는 되지 않으며, 정적 호출이 더 유연하다는 것을 의미 합니다.
표준 라이브러리는 이런 이유로 가능하면 정적으로 dispatch 하도록 하고 있습니다.

## Dynamic dispatch

Rust provides dynamic dispatch through a feature called ‘trait objects’. Trait
objects, like `&Foo` or `Box`, are normal values that store a value of
*any* type that implements the given trait, where the precise type can only be
known at runtime.

A trait object can be obtained from a pointer to a concrete type that
implements the trait by *casting* it (e.g. `&x as &Foo`) or *coercing* it
(e.g. using `&x` as an argument to a function that takes `&Foo`).

These trait object coercions and casts also work for pointers like `&mut T` to
`&mut Foo` and `Box` to `Box`, but that’s all at the moment. Coercions
and casts are identical.

This operation can be seen as ‘erasing’ the compiler’s knowledge about the
specific type of the pointer, and hence trait objects are sometimes referred to
as ‘type erasure’.

Coming back to the example above, we can use the same trait to perform dynamic
dispatch with trait objects by casting
동적 dispatch

Rust 는 ‘트레잇 객채(trait objects)’ 라는 기능을 통해 동적 dispatch 를 제공 합니다.
트레잇 객체는 `&Foo` 혹은 `Box` 와 같이 주어진 트레잇을 구현한 *어떤* 타입의 값을 저장하는 평범한 값들이며, 정확한 타입은 런타입에 결정 될 수 있습니다.

트레잇 객체는 트레잇을 구현한 구체적인 타입에 대한 포인터를 *캐스팅* 하거나 (예. `&x as &Foo`) *강제(coercing)* 해서 (예. `&Foo` 가 파라미터로 정의된 함수에 대해 인자로 `&x` 를 사용) 얻을 수 있습니다.

이러한 트레잇 객체에 대한 강제(coercion)나 캐스팅은 `&mut Foo` 나 `Box` 에 대한 포인터인 `&mut T`, `Box` 에 대해서도 모두 그 순간에 동작합니다. 강제와 캐스팅은 동일합니다.

이 작업은 특정 포인터의 타입에 대한 컴파일러의 지식을 ‘지우는’ 것 처럼 보입니다. 이런 이유로 트레잇 객체는 가끔 ‘타입 지우개(type erasure)’ 로 불립니다.

위 예제로 돌아가면, 트레잇 객체들을 동일한 트래잇으로 캐스팅 함으로써 동적 dispatch 를 수행할 수 있습니다
:

```rust
# trait Foo { fn method(&self) -> String; }
# impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } }
# impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } }

fn do_something(x: &Foo) {
x.method();
}

fn main() {
let x = 5u8;
do_something(&x as &Foo);
}
```

or by coercing혹은 강제하기:

```rust
# trait Foo { fn method(&self) -> String; }
# impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } }
# impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } }

fn do_something(x: &Foo) {
x.method();
}

fn main() {
let x = "Hello".to_string();
do_something(&x);
}
```

A function that takes a trait object is not specialized to each of the types
that implements `Foo`: only one copy is generated, often (but not always)
resulting in less code bloat. However, this comes at the cost of requiring
slower virtual function calls, and effectively inhibiting any chance of
inlining and related optimizations from occurring
트레잇 객체를 받는 함수는 `Foo` 를 구현한 각각의 타입들에 대해서 구체화 하지 않습니다: 항상 그렇지는 않지만 하나의 복사본이 생성되고 적은 코드 블로트가 발생합니다. 그러나 더 느린 가상함수 호출 비용이 발생하게 되고, 효과적으로 인라이닝(inlining) 하고 관련된 최적화가 발생하는 것을 방해합니다.

### Why pointers?

Rust does not put things behind a pointer by default, unlike many managed
languages, so types can have different sizes. Knowing the size of the value at
compile time is important for things like passing it as an argument to a
function, moving it about on the stack and allocating (and deallocating) space
on the heap to store it.

For `Foo`, we would need to have a value that could be at least either a
`String` (24 bytes) or a `u8` (1 byte), as well as any other type for which
dependent crates may implement `Foo` (any number of bytes at all). There’s no
way to guarantee that this last point can work if the values are stored without
a pointer, because those other types can be arbitrarily large.

Putting the value behind a pointer means the size of the value is not relevant
when we are tossing a trait object around, only the size of the pointer itself.

### Representation

The methods of the trait can be called on a trait object via a special record
of function pointers traditionally called a ‘vtable’ (created and managed by
the compiler).

Trait objects are both simple and complicated: their core representation and
layout is quite straight-forward, but there are some curly error messages and
surprising behaviors to discover.

Let’s start simple, with the runtime representation of a trait object. The
`std::raw` module contains structs with layouts that are the same as the
complicated built-in types, [including trait objects][stdraw]:

```rust
# mod foo {
pub struct TraitObject {
pub data: *mut (),
pub vtable: *mut (),
}
# }
```

[stdraw]: ../std/raw/struct.TraitObject.html

That is, a trait object like `&Foo` consists of a ‘data’ pointer and a ‘vtable’
pointer.

The data pointer addresses the data (of some unknown type `T`) that the trait
object is storing, and the vtable pointer points to the vtable (‘virtual method
table’) corresponding to the implementation of `Foo` for `T`.


A vtable is essentially a struct of function pointers, pointing to the concrete
piece of machine code for each method in the implementation. A method call like
`trait_object.method()` will retrieve the correct pointer out of the vtable and
then do a dynamic call of it. For example:

```rust,ignore
struct FooVtable {
destructor: fn(*mut ()),
size: usize,
align: usize,
method: fn(*const ()) -> String,
}

// u8:

fn call_method_on_u8(x: *const ()) -> String {
// the compiler guarantees that this function is only called
// with `x` pointing to a u8
let byte: &u8 = unsafe { &*(x as *const u8) };

byte.method()
}

static Foo_for_u8_vtable: FooVtable = FooVtable {
destructor: /* compiler magic */,
size: 1,
align: 1,

// cast to a function pointer
method: call_method_on_u8 as fn(*const ()) -> String,
};


// String:

fn call_method_on_String(x: *const ()) -> String {
// the compiler guarantees that this function is only called
// with `x` pointing to a String
let string: &String = unsafe { &*(x as *const String) };

string.method()
}

static Foo_for_String_vtable: FooVtable = FooVtable {
destructor: /* compiler magic */,
// values for a 64-bit computer, halve them for 32-bit ones
size: 24,
align: 8,

method: call_method_on_String as fn(*const ()) -> String,
};
```

The `destructor` field in each vtable points to a function that will clean up
any resources of the vtable’s type: for `u8` it is trivial, but for `String` it
will free the memory. This is necessary for owning trait objects like
`Box`, which need to clean-up both the `Box` allocation as well as the
internal type when they go out of scope. The `size` and `align` fields store
the size of the erased type, and its alignment requirements; these are
essentially unused at the moment since the information is embedded in the
destructor, but will be used in the future, as trait objects are progressively
made more flexible.

Suppose we’ve got some values that implement `Foo`. the explicit form of
construction and use of `Foo` trait objects might look a bit like (ignoring the
type mismatches: they’re all just pointers anyway):

```rust,ignore
let a: String = "foo".to_string();
let x: u8 = 1;

// let b: &Foo = &a;
let b = TraitObject {
// store the data
data: &a,
// store the methods
vtable: &Foo_for_String_vtable
};

// let y: &Foo = x;
let y = TraitObject {
// store the data
data: &x,
// store the methods
vtable: &Foo_for_u8_vtable
};

// b.method();
(b.vtable.method)(b.data);

// y.method();
(y.vtable.method)(y.data);
```