这是Rust的assert_eq!宏实现.为简洁起见,我只复制了第一个分支:
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)
}
}
}
});
}
Run Code Online (Sandbox Code Playgroud)
这里的目的是match什么?为什么不检查不平等?
DK.*_*DK. 47
好吧,让我们删除比赛.
macro_rules! assert_eq_2 {
($left:expr, $right:expr) => ({
if !($left == $right) {
panic!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`"#, $left, $right)
}
});
}
Run Code Online (Sandbox Code Playgroud)
现在,让我们选择一个完全随机的例子......
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);
Run Code Online (Sandbox Code Playgroud)
这将扩展到......
{
if !(really_complex_fn() == 1) {
panic!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`"#, really_complex_fn(), 1)
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,我们两次调用该函数.这不太理想,如果函数的结果每次调用时都会改变,那就更不理想了.
这match只是一种快速,简单的方法,可以将宏的"参数"恰好评估一次,并将它们绑定到变量名称.
Usingmatch确保表达式$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]);
Run Code Online (Sandbox Code Playgroud)
假设这扩展为:
let left = &vals.collect::<Vec<_>>().as_slice();
let right = &[1,2,3,4];
if !(*left == *right) {
panic!("...");
}
Run Code Online (Sandbox Code Playgroud)
在 Rust 中,语句中产生的临时变量的生命周期通常仅限于语句本身。因此,这种扩展是错误的:
let vals = vec![1, 2, 3, 4].into_iter();
assert_eq!(vals.collect::<Vec<_>>().as_slice(), [1, 2, 3, 4]);
Run Code Online (Sandbox Code Playgroud)
临时vals.collect::<Vec<_>>()需要至少与 一样长left,但实际上它在let语句的末尾被删除。
将此与扩展进行对比
match (&vals.collect::<Vec<_>>().as_slice(), &[1,2,3,4]) {
(left, right) => {
if !(*left == *right) {
panic!("...");
}
}
}
Run Code Online (Sandbox Code Playgroud)
这会产生相同的临时变量,但它的生命周期扩展到整个匹配表达式——足够长的时间让我们比较left和right,并在比较失败时将它们插入到错误消息中。
从这个意义上说,match是 Rust 的let ... in构造。
请注意,这种情况在非词法生命周期中没有改变。尽管它的名字,NLL 不会改变任何值的生命周期——即当它们被删除时。它只会使借用的范围更加精确。所以在这种情况下它对我们没有帮助。
| 归档时间: |
|
| 查看次数: |
5570 次 |
| 最近记录: |