Any 트레잇과 TypeId

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

Any 타입을 사용하는 예제:

#![allow(unused)]
fn main() {
use std::any::Any;

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

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

#![allow(unused)]
fn main() {
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 함수는 다음과 같이 정의되어 있습니다:

#![allow(unused)]
fn main() {
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가 존재하지만 깊이 들어가는 기능이기도 하고, 무엇보다 실험적 기능이므로 이 글에선 서술하지 않습니다. 혹시라도 궁금하다면 이곳을 참고해봅시다.