LoginSignup
3
3

More than 3 years have passed since last update.

std::any::AnyとBoxで何でも受け入れる構造体を作成する

Posted at

はじめに

ソースコードはgistを参照してください。

やりたいこと

様々な型を受け入れる構造体をつくりたい

問題

様々な型に対応するにはジェネリクスを使うのがよいですが、以下のようなコードはコンパイル不可能です。
b_vecの型は1つ目のアイテムを解釈した瞬間から、Vec<B<i32>>となってしまい、B<String>を格納することはできません。

struct B<T> {
    value: T
}

fn main() {
    let b_vec = vec![
        B {
            value: 10 as i32,
        },
        B {
            value: String::from("hi"),
        }
    ];
}

解決

std::any::AnyトレイトとBoxを組み合わせたメンバを構造体に定義することで解決できます。
std::any::Anyトレイトは非'static参照を含む型には実装できませんが、逆に言うとほとんどの型で使用することができます。(実装されています)

そのため、std::any::Anyトレイトオブジェクトにより様々な型を指定することが可能となります。

以下のようにして、Boxの中にstd::any::Anyトレイトオブジェクトを入れた型のメンバを構造体Aに定義します。すると、あらゆる型をもったAでVectorを生成してもVec<Box<dyn Any>>となり、多様な型を受け入れられるようになります。

また、std::any::Anyトレイトにはdowncast_refメソッドがあり、これで元の型を取り戻すことができます。それもOption型で取り出すことができるので大変便利です。

use std::any::Any;

struct A {
    value: Box<dyn Any>
}

fn main() {
    let a_vec = vec![
        A {
            value: Box::new(10),
        },
        A {
            value: Box::new(String::from("hi")),
        },
    ];
    for v in a_vec.iter() {
        if let Some(a) = v.value.downcast_ref::<i32>() {
            println!("i32 {}", a);
        } else if let Some(a) = v.value.downcast_ref::<String>() {
            println!("string {}", a)
        }
    }
}

まとめ

std::any::AnyトレイトとBoxを組み合わせることにより、まるで動的型付けのようなコードを書くことができるようになりました。

参考

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3