这是Rust的assert_eq!
macro implementation。为简洁起见,我只复制了第一个分支:
macro_rules! assert_eq {
($left:expr, $right:expr) => ({
match (&$left, &$right) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
panic!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`"#, left_val, right_val)
}
}
}
});
}
这里match
的目的是什么?为什么不检查不平等?
好吧,让我们删除比赛。
macro_rules! assert_eq_2 {
($left:expr, $right:expr) => ({
if !($left == $right) {
panic!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`"#, $left, $right)
}
});
}
现在,让我们选择一个完全随机的例子......
fn really_complex_fn() -> i32 {
// Hit the disk, send some network requests,
// and mine some bitcoin, then...
return 1;
}
assert_eq_2!(really_complex_fn(), 1);
这将扩展到......
{
if !(really_complex_fn() == 1) {
panic!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`"#, really_complex_fn(), 1)
}
}
如您所见,我们两次调用该函数。这不太理想,如果函数的结果每次调用时都会改变,那就更不理想了。
match
只是一种快速,简单的方法,可以精确地评估宏的“参数”并将它们绑定到变量名称。
使用match
确保表达式$left
和$right
每次仅被评估一次,并且在评估期间创建的任何临时值至少与结果绑定left
和right
一样长。
多次使用$left
和$right
的扩展 - 一次执行比较时,再次插入错误消息时 - 如果任一表达式具有副作用,则会出现意外行为。但为什么扩张不能像let left = &$left; let right = &$right;
那样?
考虑:
let vals = vec![1, 2, 3, 4].into_iter();
assert_eq!(vals.collect::<Vec<_>>().as_slice(), [1, 2, 3, 4]);
假设这扩展到:
let left = &vals.collect::<Vec<_>>().as_slice();
let right = &[1,2,3,4];
if !(*left == *right) {
panic!("...");
}
在Rust中,声明中生成的临时生命的生命周期通常仅限于声明本身。因此,这个扩展is an error:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:5:21
|
5 | let left = &vals.collect::<Vec<_>>().as_slice();
| ^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value dropped here while still borrowed
| |
| temporary value does not live long enough
暂时的vals.collect::<Vec<_>>()
需要至少与left
一样长,但实际上它在let
声明结束时被删除。
将此与扩展进行对比
match (&vals.collect::<Vec<_>>().as_slice(), &[1,2,3,4]) {
(left, right) => {
if !(*left == *right) {
panic!("...");
}
}
}
这会生成相同的临时值,但它的生命周期会扩展到整个匹配表达式 - 足够长,以便我们比较left
和right
,并在比较失败时将它们插入到错误消息中。
从这个意义上讲,match
是Rust的let ... in
构造。
请注意,这种情况与non-lexical lifetimes不变。尽管它的名字,NLL并没有改变任何值的生命周期 - 即它们被丢弃时。它只会使借用范围更加精确。所以在这种情况下它并没有帮助我们。