Rustのマクロで識別子(ident)を文字列に変換する
以前マクロの練習のためにHashMapやBTreeMapを生成するマクロを作った。
マクロ箇所だけ抽出するとこんな感じ
macro_rules! hash_map { ($($k:expr => $v:expr),*) => {{ let mut _m = std::collections::HashMap::new(); $(_m.insert($k, $v);)* _m }}; }
JavaScriptのオブジェクトリテラルのように キーはダブルクォートで囲わない記法ができると格好良い。
let m = hash_map!(a => 1, b => 2, c => 3); println!("{:#?}", m);
{ "b": 2, "c": 3, "a": 1, }
これを行うにはmacro_rulesで識別子を文字列に変換する必要あり。 結論としては stringifyマクロを使えばOK。C#のnameof式に近い動きをしてくれそう。
macro_rules! hash_map { ($($ik:ident=> $v:expr),*) => {{ let mut _m = std::collections::HashMap::new(); $(_m.insert(stringify!($ik), $v);)* _m }}; }
キーが文字列でも識別子でも動くようにしたバージョン
macro_rules! hash_map { ($($ik:ident => $v:expr),*) => { hash_map!($(stringify!($ik) => $v),*) }; ($($k:expr => $v:expr),*) => {{ let mut _m = std::collections::HashMap::new(); $(_m.insert($k, $v);)* _m }}; }
exprのほうを上に持ってくると動作しなくて少しハマった。多分識別子で指定したマクロはexprのほうにもマッチするからだと思う。
参考までにHashMapやBTreeMapを作るクレートは既存のものが存在する。
特別な事情がない限りこちらを使うべきでしょう。こちらのマクロはキャパシティを計算してくれるし。