这是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
的目的是什么?为什么不检查不平等?
答案 0 :(得分:47)
好的,让我们删除比赛。
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
只是一种快速,简单的方法,可以将宏的“参数”恰好评估一次并将它们绑定到变量名称。
答案 1 :(得分:2)
使用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!("...");
}
}
}
这会产生相同的临时变量,但是其生存期会遍及整个match表达式-足够长的时间供我们比较left
和right
,如果比较失败,则会将它们插值到错误消息中。
从这个意义上说,match
是Rust的let ... in
构造。
请注意,non-lexical lifetimes的情况不变。尽管名称如此,但NLL不会更改任何值的生命周期-即何时删除它们。这只会使借贷的范围更加精确。因此,在这种情况下对我们没有帮助。