RustでPHPのような連想配列を作った

#概要
RustでHashMapを用いたPHP風の連想配列を作りました。
ほとんどの記事で、HashMapに特定の型のみを用いた使い方しかしていなかったので（というかそれで十分なのだろうけれど）、どんな型でも入るHashMapを作りました。

###経緯（飛ばしてもOK）
ironライブラリを用いてWebプログラミングの勉強をしていたとき、PHPのセッション配列がほしくなりました。
最初はenumに`Integer(i64)`や`Bool(bool)`といったヴァリアントを持たせてHashMapに入れていたのですが、
`if let Some(Value::Integer(value)) = ...;`
と書いているとどうしても冗長に感じてきて、別の方法をとることにしました。

###実装
といっても、使うのは`Any`トレイトだけです。`Box`に包んで使います。

```Rust
use std::any::Any;
use std::collections::HashMap;

let mut hashmap = HashMap::new();
hashmap.insert(
    "key".to_owned(),
    Box::new(0) as Box<Any>
);
```

関数にしていますが、値を取得する際は次のような処理になります。

```Rust
// （Boxの中身の）値のイミュータブルな参照を返す
fn get_ref<'a, T: 'static>(hashmap: &'a HashMap<String, Box<Any>>, key: &str) -> Option<&'a T> {
    if let Some(ref_boxed_value) = hashmap.get(key) {
        let ref_value: &Any = ref_boxed_value.as_ref();
        if let Some(ref_value) = ref_value.downcast_ref::<T>() {
            return Some(ref_value);
        }
    }
    None
}

// （Boxの中身の）値のミュータブルな参照を返す
fn get_mut_ref<'a, T: 'static>(hashmap: &'a mut HashMap<String, Box<Any>>, key: &str) -> Option<&'a mut T> {
    if let Some(mut_ref_boxed_value) = hashmap.get_mut(key) {
        use std::borrow::BorrowMut;
        let mut_ref_value: &mut Any = mut_ref_boxed_value.borrow_mut();
        if let Some(mut_ref_value) = mut_ref_value.downcast_mut::<T>() {
            return Some(mut_ref_value);
        }
    }
    None
}

// 次の二つはCloneトレイトを実装している必要がある
// （Boxの中身の）値のコピーを返す
fn get<T: 'static + Clone>(hashmap: &HashMap<String, Box<Any>>, key: &str) -> Option<T> {
    if let Some(ref_boxed_value) = hashmap.get(key) {
        let ref_value: &Any = ref_boxed_value.as_ref();
        if let Some(ref_value) = ref_value.downcast_ref::<T>() {
            let value = (*ref_value).clone();
            return Some(value);
        }
    }
    None
}

// HashMapから値を取り出す
// ※正確には、HashMapからremoveしたBoxの中身の値のコピーを返しています
fn remove<T: 'static + Clone>(hashmap: &mut HashMap<String, Box<Any>>, key: &str) -> Option<T> {
    if let Some(boxed_value) = hashmap.remove(key) {
        let ref_value: &Any = boxed_value.as_ref();
        if let Some(ref_value) = ref_value.downcast_ref::<T>() {
            let value = (*ref_value).clone();
            return Some(value);
        }
    }
    None
}
```
Boxの中身を直接取り出す方法がわからなかったので、`get()`と`remove()`は一度参照を受け取ってからそれをクローンしています。
そのため、独自の構造体を扱う場合は適宜`#[derive(Clone)]`してやってください。`get_ref()`と`get_mut_ref()`だけであれば必要ありません。

###使用例
```Rust
let mut hashmap = HashMap::new();
hashmap.insert("key".to_owned(), Box::new(0) as Box<Any>);

// 値のイミュータブルな参照を得る(Boxの中身のイミュータブルな参照を得る)
if let Some(ref_value) = get_ref::<u8>(&hashmap, "key") {
    assert_eq!(ref_value, &0);
} else {
    panic!("Error1");
}

// 値のミュータブルな参照を得る(Boxの中身のミュータブルな参照を得る)
if let Some(mut_ref_value) = get_mut_ref::<u8>(&mut hashmap, "key") {
    assert_eq!(mut_ref_value, &mut 0);
    *mut_ref_value += 1;
    assert_eq!(mut_ref_value, &mut 1);
} else {
    panic!("Error2");
}

// 値のコピーを得る(Boxの中身のコピーを得る)
if let Some(value) = get::<u8>(&hashmap, "key") {
    assert_eq!(value, 1);
} else {
    panic!("Error3");
}

// 値を取り出す(Boxの中身のコピーを得、HashMapから要素はなくなる)
if let Some(value) = remove::<u8>(&mut hashmap, "key") {
    assert_eq!(value, 1);
} else {
    panic!("Error4");
}
assert!(!hashmap.contains_key("key"));
```

###制約
`Any`トレイトを実装している必要があるので、非'staticな参照もしくはそれを含む型を扱うことはできません。
[こちら](https://qiita.com/taskie/items/3b89962582124138a64b)の記事にて詳しく解説されています。

##まとめ
型に厳しいRustだと連想配列は無理かな？と思っていたけれど、Boxが役立ちました。ただヒープに保存されるので高速な処理には向いていないと思われます。詳しくはないですが...