포스트

Any 트레잇과 TypeId

Any trait and TypeId

Any 트레잇과 TypeId

개요

Any 트레잇은 모든 'static 타입의 동적 타이핑을 가능케 하는 트레잇입니다.
'static은 수명 'static과 트레잇 바운드 'static이 존재합니다. 자세한 내용은 이 글을 참고해봅시다.
여기서 서술하는 'static은 트레잇 바운드 'static을 의미합니다.

Any 타입을 사용하는 예제:

1
2
3
4
use std::any::Any;

let x: Box<dyn Any> = Box::new(1);
let y: &dyn Any = &1;

Any는 컴파일 타임에 크기를 알 수 없습니다. 때문에 dyn 접두사를 사용하며, Box 또는 'static 참조를 사용합니다.
Any 타입을 사용하는 다른 예제:

1
2
3
4
5
6
7
8
9
10
11
use std::{any::Any, fmt::Display};

fn foo<T: Any + Display>(x: &T) {
    match (x as &dyn Any).downcast_ref::<i32>() {
        Some(v) => println!("i32: {v}"),
        None => println!("unknown: {x}"),
    }
}

foo(&1);
foo(&"hello");

여기서 downcast_ref를 사용하였습니다.

downcast_mut 등의 여러 API가 존재하는데, 각자 하는 역할은 다음과 같습니다:

  • downcast<T>: 이는 Box<...>에 대해 구현되어 있습니다. Boxdowncast 하며, Result<T, E>를 반환합니다.
  • downcast_ref<T>: dyn Any + 'static에 구현되어 있습니다. 내부 값이 T 타입과 일치한다면, 그것에 대해 참조를 반환합니다. Option<T>를 반환하며, 일치하지 않다면 None을 반환합니다.
  • downcast_mut<T>: 위 downcast_ref와 동일하며, 가변 참조를 반환합니다.

이밖에도 is 함수 등이 구현되어 있지만, 이 글에선 서술하지 않습니다. 자세한 내용은 이곳을 참고해봅시다.

TypeId

TypeId는 타입에 대해 고유한 식별자를 나타냅니다. 위에서 서술하진 않았지만, 타입을 비교하는 is 함수는 다음과 같이 정의되어 있습니다:

1
2
3
4
5
6
7
pub fn is<T: Any>(&self) -> bool {
    let t = TypeId::of::<T>();

    let concrete = self.type_id();

    t == concrete
}

대충 보면 is의 사용 방법을 알 것입니다. 하지만 is의 사용 방법을 서술하진 않았으니, TypeId 부분을 봅시다.
Any 트레잇은 TypeId를 반환하는 type_id라고 하는 함수를 구현하고 있습니다.

위는 TypeId::of::<T>()를 사용하여, 두 TypeId가 일치하는지 검사하는 함수입니다.
사실상 TypeId의 주요 기능은 이게 전부입니다.

이밖에도 ProviderDemand가 존재하지만 깊이 들어가는 기능이기도 하고, 무엇보다 실험적 기능이므로 이 글에선 서술하지 않습니다.
혹시라도 궁금하다면 이곳을 참고해봅시다.