match 和 unwrap 之间的不同行为

Different behavior between match and unwrap

我做了一个小程序,它显示了一个我无法解释的奇怪行为。 我正在使用 rodio crate 来尝试一些音频内容。

我已经完成了两个程序,在我看来,它们应该给出相同的结果。

我第一个用匹配来处理错误:

let sink : Option<Sink> = match rodio::OutputStream::try_default() {
        Ok((_, handle)) => {
            match Sink::try_new(&handle) {
                Ok(s) => Some(s),
                Err(_e) => None,
            }
        },
        Err(_e) => None,
};
println!("{}", sink.unwrap().len());

在最后一个中,我使用了 unwrap 代替。

let (_, handle) = rodio::OutputStream::try_default().unwrap();
let s = Sink::try_new(&handle).unwrap();
println!("{}",s.len());

第一个按预期执行 print 语句,而最后一个在第二个 unwrap 中出现 panic。

一旦没有错误传播、隐式转换或其他可以解释的东西,我就很奇怪。这里的问题与错误本身无关,而是两个代码之间的差异。

问题是 范围 和 rodio 的实施细节之一:这里的一个关键项目是 OutputStream::try_default(),您如何处理 Sink::try_new(&handle) 它将始终表现相同,而不是 try_default,如果你匹配或 if let 它会工作正常,如果你 unwrap 它会失败。

但为什么会这样,两者应该是等价的。答案在 rodio 的细节中,特别是 OutputStreamHandle:

pub struct OutputStreamHandle {
    mixer: Weak<DynamicMixerController<f32>>,
}

所以 OutputStreamHandle(之后是 OSH)只持有 DynamicMixerController 的弱引用,OutputStream(之后是 OS)一个强有力的参考。

这意味着 OSH 仅在 OS 存活时“有效”。

let (_, handle) = OutputStream::try_default().unwrap();

没有name OS,所以没有抓住它,它立即掉落,Arc被释放并且OSH什么都不抓,沉不开心

那另一个怎么用呢?因为

    if let Ok((_, handle)) = OutputStream::try_default() {
        let sink = Sink::try_new(&handle).unwrap();
        println!("match {}", sink.len());
    }

确实比较

{
    let _var = OutputStream::try_default();
    if let Ok((_, handle)) = _var {
        let sink = Sink::try_new(&handle).unwrap();
        println!("match {}", sink.len());
    }
}

所以 match/if let 本身让 Result 保持活力,这是一种祝福(但 )。

由于 Result 保持活动状态,元组保持活动状态,OutputStream 保持活动状态,Arc 保持活动状态,因此 OS H 有一个可用的 mixer,接收器可以使用它。

您可以通过将 OutputStream 绑定到“正确的”名称来解决第二个版本的问题,例如

let (_stream, handle) = OutputStream::try_default().unwrap();

在名称前加上 _ 将创建真实的绑定,但会抑制“未使用的变量”警告。

FWIW 小心处理这类事情对于 RAII 类型非常重要,RAII 类型的值是您“不需要”的,最常见的互斥体保护 code 而不是数据:

let _ = m.lock().unwrap();

不做任何事情,互斥保护没有保持活动状态,所以锁立即释放。在那种情况下,你宁愿

let _lock = m.lock().unwrap():

甚至更好

let lock = m.lock().unwrap();
...
drop(lock);