Rust の std::any::Any トレイトを用いて安全に動的型付けっぽいことをやる

# 概要

Rust の `std::any` モジュールには **`Any`** という名前のトレイトがあります（ここで `boost::any` を使ったことのある C++er は何かを察する）。`Any` トレイトはほとんどあらゆる型に実装されるため、すなわち**ほとんどあらゆる型の参照を `&Any` で受けることができます。**

* [std::any - Rust](https://doc.rust-lang.org/std/any/index.html)
    * [std::any::Any - Rust](https://doc.rust-lang.org/std/any/trait.Any.html)

`Any` は “A type to emulate dynamic typing” （動的型付けをエミュレートする型）と公式リファレンスで説明されています。これを用いることで**「あらゆる型の参照が入る変数を定義する」「実行時の型によって処理を分岐させる」**などといった動的型付けっぽいプログラミングが可能となります。しかもダウンキャスト（`Any` から指定の型への変換[^1]）の結果は `Option` 型で返ってくるため、パターンマッチを使うことで分岐処理を型安全に、かつ少ない記述量で書くことができます。

[^1]: ダウンキャストは他言語では「基底クラスから派生クラスへの型変換」の意。C++ だと `dynamic_cast`。Rust の場合クラスの継承は機能として存在しないので、ここでは「抽象型（トレイト）から具象型への変換」を指す。

あらかじめ入る型の候補が分かっている場合には値持ちの `enum` を使う方が筋がよさそうではあるけれど[^2]、`&Any` も一つの手段として覚えておくといざという時に役に立ちそうですね。

[^2]: `boost::any` より `boost::variant` の方が基本的に扱いやすいのと同様。

# 解説

（以下のコード例の動作は [Wandbox](http://melpon.org/wandbox) の Rust HEAD 1.15.0-dev (5f128ed10 2016-12-06) で確認しています。[実際のコードはこちら](http://melpon.org/wandbox/permlink/K31RiiacMOsrdFMy)）

`&Any` 型のミュータブルな変数 `a` に色々な型の参照を突っ込む例です。**`is::<T>`** メソッドで `&Any` が指定の型 `T` の値を参照しているかどうかを判定できます。

```rust
use std::any::Any;

#[derive(Debug)] enum E { H, He, Li, Be }
struct S { x: u8, y: u8, z: u16 }

fn main() {   
    let v1 = 0xc0ffee_u32;
    let v2 = E::He;
    let v3 = S { x: 0xde, y: 0xad, z: 0xbeef };
    let v4 = "hoge";

    let mut a: &Any;

    a = &v1;
    assert!(a.is::<u32>());
    a = &v2;
    assert!(a.is::<E>());
    a = &v3;
    assert!(a.is::<S>());
    a = &v4;
    assert!(a.is::<&str>());
}
```

**`downcast_ref::<T>`** メソッドを使うと指定の型へのダウンキャストができます。結果は `Option<&T>` 型で返ってくるため、`if let` を使うと型による処理の分岐をいー感じに書けます。

```rust
fn print_any(a: &Any) {
    if let Some(v) = a.downcast_ref::<u32>() {
        println!("u32 {:x}", v);
    } else if let Some(v) = a.downcast_ref::<E>() {
        println!("enum E {:?}", v);
    } else if let Some(v) = a.downcast_ref::<S>() {
        println!("struct S {:x} {:x} {:x}", v.x, v.y, v.z);
    } else {
        println!("else!");
    }
}

fn main() {
    print_any(& 0xc0ffee_u32);                       // => u32 c0ffee
    print_any(& E::He);                              // => enum E He
    print_any(& S { x: 0xde, y: 0xad, z: 0xbeef });  // => struct S de ad beef
    print_any(& "hoge");                             // => else!
}
```

# 制限

## 非 `'static` な参照を含む型には使えない

非 `'static` な参照を含む型は `Any` トレイトを実装しないため、その参照を `&Any` で受けることはできません（コンパイルエラーになります）。

```rust
struct T<'a> { x: &'a i32 }

fn main() {
    let answer = 42;
    let v = T { x: &answer };
    let mut a: &Any;
    // a = &v;  // Compile Error!
    // (note: borrowed value must be valid for the static lifetime...)
}
```

以下のように `'static` な参照であればコンパイルが通ります。

```rust
static ANSWER: i32 = 42;

fn main() {
    let v = T { x: &ANSWER };
    let mut a: &Any;
    a = &v;
    assert!(a.is::<T>());
}
```

## トレイトへのダウンキャストはできない

ダウンキャストの変換先は具象型に限定され、トレイトへの変換はできません。つまり `a.downcast_ref::<Display>()` とか `a.downcast_ref::<Clone>()` とか `a.downcast_ref::<Eq>()` とかはムリ。

# その他の機能

## `downcast_mut` メソッド

ミュータブルな参照にダウンキャストする **`downcast_mut`** メソッドも用意されています。

```rust
let mut answer = 0;
{
    let a: &mut Any;
    a = &mut answer;
    if let Some(x) = a.downcast_mut::<i32>() {
        *x = 42;
    }
}
assert_eq!(answer, 42);
```

## `Box<Any>` 型

`&Any` の代わりに **`Box<Any>`** を使うこともできます。こちらの場合中身の値はヒープ領域上に配置されます。

* [std::boxed::Box - Rust](https://doc.rust-lang.org/std/boxed/struct.Box.html)

`Box<Any>` には `downcast<T>` メソッドが用意されています。戻り値は `Result<Box<T>, Box<Any>>` です。

```rust
let mut a: Box<Any>;
a = Box::new(42);
if let Ok(answer) = a.downcast::<i32>() {
    assert_eq!(*answer, 42);
}
```

## `TypeId::of::<T>` 関数

**`std::any::TypeId::of::<T>`** 関数で各具象型について一意に定まる **`TypeId`** を得ることができます。

* [std::any::TypeId - Rust](https://doc.rust-lang.org/std/any/struct.TypeId.html)

```rust
use std::any::TypeId;
println!("{:?}", TypeId::of::<u8>());    // => TypeId { t: 7100172564015177708 }
println!("{:?}", TypeId::of::<&str>());  // => TypeId { t: 3020856465566936866 }
```

`TypeId` は opaque（不透明）なオブジェクトであり、内部の具体的な値に依存したコードを書いてはいけません（デバッグプリントすると中身見えちゃうけど）。`Eq`, `Hash` トレイトを実装しているので `TypeId` 同士を比較したり `TypeId` を `HashMap` のキーとして使ったりすることなどは可能です。

## `get_type_id` メソッド (unstable)

`Any` トレイトの **`get_type_id`** メソッドを用いてそのオブジェクトの `TypeId` を得ることができます。**ただし unstable です。**そのうち関連関数（静的メソッド）に置き換えられるらしい。

```rust
#![feature(get_type_id)]

fn main() {
    use std::any::Any;
    println!("{:?}", (&0u8 as &Any).get_type_id());  // => TypeId { t: 7100172564015177708 }
    println!("{:?}", (&"" as &Any).get_type_id());   // => TypeId { t: 3020856465566936866 }
}
```

# 内部動作

`'static` な型 `T` は `Any` トレイトを以下のように実装します。`TypeId::of::<T>` 関数は内部でコンパイラ組み込みの `std::intrinsics::type_id::<T>` 関数を呼びます。

```rust
impl<T: 'static + ?Sized> Any for T {
    #[unstable(feature = "get_type_id",
               reason = "this method will likely be replaced by an associated static",
               issue = "27745")]
    fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}
```

`downcast_ref` の実装は「`self.is::<T>()`（すなわち `self.get_type_id() == TypeId::of::<T>()`）が真なら `&T` にキャストして `Some` に包んで返す」というシンプルな実装です。

```rust
impl Any {
    ...
    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
        if self.is::<T>() {
            unsafe { Some(&*(self as *const Any as *const T)) }
        } else {
            None
        }
    }
    ...
}
```

# まとめ

Rust でいわゆる実行時型情報 (RTTI, RunTime Type Identification) を扱う方法でした。Rust は色々と厳格な言語であるという印象が強いけど、こういうゆるふわっぽいこともできるよ（しかも型安全に！）、ということで紹介しました。