😊

Rustでどんな値型も格納できるHashMapを実装する

2021/09/12に公開

std::any::Anyを使ってどんな値も格納できるHashMapを実装する方法をまとめます。

実現方法の要点としては以下。

  • HashMapの値型にBox<dyn Any>もしくは&'a dyn Anyを指定する
    • Box<dyn Any>の場合は値はヒープに格納され、&'a dyn Anyの場合は値のライフタイムに依存します。
  • 型パラメータにstd::any::Any制約を付ける

Box<dyn Any>

Box版の実装コードは以下です。

pub struct Config {
  values: HashMap<String, Box<dyn Any>>,
}

impl Config {
  pub fn new() -> Self {
    Self {
      values: HashMap::new(),
    }
  }

  pub fn set<T>(&mut self, k: String, v: T)
  where
    T: Any,
  {
    self.values.insert(k, Box::new(v));
  }

  pub fn get<T>(&self, key: String) -> Option<&T>
  where
    T: Any,
  {
    self.values.get(&key).and_then(|v| v.downcast_ref::<T>())
  }

}

使い方。

let mut config = Config::new();
let key = "aaa".to_string();
let value = "bbb".to_string()
config.set(key.clone(), value);
let s = config.get::<String>(key);
println!("result = {:?}", s);

&'a dyn Any

Boxを使わない場合の実装コードは以下です。

pub struct Config<'a> {
  values: HashMap<String, &'a dyn Any>,
}

impl<'a> Config<'a> {
  pub fn new() -> Self {
    Self {
      values: HashMap::new(),
    }
  }

  pub fn set<T>(&mut self, k: String, v: &'a T)
  where
    T: Any,
  {
    self.values.insert(k, v);
  }

  pub fn get<T>(&self, key: String) -> Option<&T>
    where
    T: Any,
  {
    self.values.get(&key).and_then(|&v| v.downcast_ref::<T>())
  }

}

使い方

let mut config = Config::new();
let key = "aaa".to_string();
let value = "bbb".to_string();
config.set(key.clone(), &value);
let s = config.get::<String>(key);
println!("result = {:?}", s);

とても簡単!

参考リンク

https://qiita.com/taskie/items/3b89962582124138a64b
https://gist.github.com/sile/0615a6daa1f5576d63c8775cc5a94f88

Discussion