[Rust] 다운캐스팅

Rust에서 권장되는 형태의 다운캐스팅 동작은 단순히 "트레잇 메서드"만을 사용하는 것이다.

예를 들어 아래와 같은 공통 인터페이스가 트레잇으로 있고

이런식으로 타입이 두개 있다면

트레잇 object로 값을 다루더라도, 트레잇 메서드를 이용해서 다형적인 동작이 되게 할 수 있기 떄문이다.

같은 "dyn TestTrait" 타입으로 받아서 관리하더라도, 각각의 타입에 맞게 잘 동작하는 것을 확인할 수 있다.




Any를 이용한 다운캐스팅

하지만 경우에 따라서는 직접 다운캐스팅을 해서, 그 구조체에만 존재하는 필드나 메서드를 꺼내써야할 수도 있다.

대표적으로 오류타입 같은게 그렇다. dyn Error로 되어있는걸 내부 오류 정보를 꺼내와서 처리해야할 때가 종종 있더라.

그럴때는 먼저 값을 Any의 트레잇 객체로 래핑해야 한다.

Any는 모든 타입을 동적으로 담을 수 있는 일종의 리플렉션 타입인데, C++의 RTTI 기능과 비슷하다고 보면 된다.
타입 식별을 위해서 원래 데이터 이외에도 TypeID라는 타입 식별용 비트값을 추가한다.

그리고 TypeID 값이 있기 떄문에, downcast_ref 함수를 사용하면 안전하게 다운캐스팅을 수행할 수 있다.

다운캐스팅이 실패한다면 Option::None 값을 반환한다.

이를 통해 타입 안전성을 보장할 수 있다.




unchecked 다운캐스팅 (unsafe)

Any의 일반적인 다운캐스팅은 Any 내부에 저장된 TypeId 비교를 통해 안전성을 보증한다.
하지만, 그 비교시간마저 아깝다면.. unchecked 함수를 쓰는 법밖에 없다.
아니면 raw pointer를 쓰거나.

unchecked 버전의 다운캐스팅은 unsafe이면서도, 1.68.1 현재 기준으로는 unstable한 기능이다.
이 기능을 사용하려면 툴체인을 nightly로 바꾸고 기능 활성화 매크로를 달아야 한다.

그러면 사용할 수 있다.
unchecked 버전은 검사를 수행하지 않기 때문에 Option을 반환하지 않는다.

실패해도 강제로 캐스팅을 해서 오류가 나지 않고, 예측불허한 값이 나온다.

저 값은 뭐 저 문자열의 코드값이 아무렇게나 잘려나온 것이다. unsafe이기 때문이다.

Any가 차지하는 TypeId의 저장공간마저도 아깝다면, 그냥 생포인터를 써야한다.
into_raw 등으로 생포인터를 꺼내와서, as로 강제캐스팅을 해서 쓰면 된다.

성능이야 좋겠지만 이것도 마찬가지로 아주 위험하다.

unchecked와 마찬가지로 캐스팅에 실패해도 unsafe하게 동작하기 때문이다.

이런건 정말 불가피한 상황에만 쓰자.

Any만 해도 성능이 나쁘지는 않다. 극한상황이 아니라면 체감하기 어려울 것이다.



참조
https://ysantos.com/blog/downcast-rust
https://doc.rust-lang.org/std/any/index.html