当前位置:嗨网首页>书籍在线阅读

10-宏的高级应用-为HashMap的初始化编写DSL

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

9.8 宏的高级应用——为HashMap的初始化编写DSL

了解了重复和标记树类型的知识,让我们使用重复特性在macro_rules!宏中构建一些实用的内容。在本节中,我们将构建一个暴露宏的程序,它使用户能够像下列代码那样创建HashMap:

let my_map = map! {
    1 => 2,
    2 => 3
};

与手动调用HashMap::new(),后跟一个或多个insert调用相比,这样更简洁和易读。让我们通过运行cargo new macro_map --lib命令创建一个新项目,然后为macro_rules!宏初始化一个代码块:

// macro_map/lib.rs
#[macro_export]
macro_rules! map {
    // todo
}

由于希望用户使用我们的宏,所以需要在宏定义上添加#[macro_export]属性。默认情况下,宏在模块中是私有的,与其他元素类似。我们将会调用自定义的map!宏,因为我们正在使用自己的语法来初始化HashMap,所以将采用k=>v语法,其中k表示键,v表示HashMap中的值。以下是我们在map!中的实现:

macro_rules! map {
    ( $( $k:expr => $v:expr ),* ) => {
        {
            let mut map = ::std::collections::HashMap::new();
            $(
                map.insert($k, $v);
            )*
            map
        }
    };
}

让我们对上述匹配规则进行解释。首先,我们将检查内部的内容,即($k:expr => $v:expr )。让我们将规则的这一部分用Y表示。Y通过在键k和值v之间使用=>来捕获它们,并将其类型指定为expr。围绕着Y,我们有($(Y),),表示重复Y零次或更多次,并用逗号进行分隔。在花括号内匹配规则的右侧,我们首先创建了一个HashMap实例。然后,我们编写重复器$(),其中包含我们的map.insert($k, $v)代码片段,它们将重复与我们的宏输入相同的次数。

让我们快速地编写一个测试:

// macro_map/lib.rs
#[cfg(test)]
mod tests {
    #[test]
    fn test_map_macro() {
        let a = map! {
            "1" => 1,
            "2" => 2
        };
        assert_eq!(a["1"], 1);
        assert_eq!(a["2"], 2);
    }
}

通过运行cargo test命令,我们得到以下输出结果:

running 1 test
test tests::test_map_macro ... ok

我们的测试顺利通过。接下来我们就可以使用新鲜出炉的map!宏以简捷的方式初始化HashMap了。