中身を型で表現するコンテナを作ろうとした

結果

自分には思い通りのものを作ることはできなかった。

やりたいこと(やりたかったこと)

以下みたいなことを実現したい。

  • 型をキーとした HashMap
  • 値を与えると格納される
  • 型を指定することで値を取り出せる
  • 保管していない型を指定した場合は コンパイルエラー が発生
let bag = Basket::new();
bag.set("Universe");
bag.set(42);

println!("i32: {}", bag.get::<i32>());   // => "i32: 42"
println!("&str: {}", bag.get::<&str>()); // => "&str: Universe"

// println!("bool: {}", bag.get::<bool>()); => Compile Error!

actix-webの柔軟なリクエストハンドラの仕組み

きっかけ

業務で Rust を使っていこうということになったので、Rust を勉強しながら何か Web アプリでも作ってみようと思って actix-web を触り始めた。サンプルコードをいじっていて気になったのはリクエストハンドラの引数の柔軟さで、まるでスクリプト言語のように扱える。

例えば以下は http://localhost:8080/hoge にリクエストを受けると path 部分である /hoge をレスポンスとして返す。

fn index(
    req: HttpRequest,
) -> impl IntoFuture<Item = String, Error = Error> {
    Ok(String::from(req.path()))
}

fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .default_service(web::route().to_async(index))
    })
    .bind("127.0.0.1:8080")?
    .run()
}

リクエストハンドラに何かデータ(普通はアプリケーションの状態など)を渡すこともできる。この例だと http://localhost:8080/spacecat にアクセスすると http://www.example.com/spacecat が表示される。

fn index(
    req: HttpRequest,
    prefix: web::Data<&str>,
) -> impl IntoFuture<Item = String, Error = Error> {
    Ok(format!("{}{}", prefix, req.path()))
}

fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .data("http://www.example.com")
            .default_service(web::route().to_async(index))
    })
    .bind("127.0.0.1:8080")?
    .run()
}

ここで「どこまで柔軟性があるんだろうか?」と思って、試しにリクエストハンドラの引数の順番を入れ替えてみた。

fn index(
    prefix: web::Data<&str>,
    req: HttpRequest,
) -> impl IntoFuture<Item = String, Error = Error> {
    Ok(format!("{}{}", prefix, req.path()))
}

これ、ちゃんと動いてしまう。

Rust のようなガチガチに検証するタイプの言語でこの柔軟性を一体どうやって実現しているんだろう・・・? と思ったのがきっかけ。