Gab*_*ori 5 error-handling rust
我做了一个小程序,它显示了一种我无法解释的奇怪行为。我正在使用 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());
Run Code Online (Sandbox Code Playgroud)
在上一篇中,我使用了 unwrap 来代替。
let (_, handle) = rodio::OutputStream::try_default().unwrap();
let s = Sink::try_new(&handle).unwrap();
println!("{}",s.len());
Run Code Online (Sandbox Code Playgroud)
第一个按预期执行打印语句,而最后一个在第二次解包时出现恐慌。
一旦没有错误传播、隐式转换或其他可以解释这一点的东西,这对我来说就很奇怪。这里的问题与错误本身无关,而是与两个代码之间的差异有关。
问题是rodio 的范围界定和实现细节之一:这里的一个关键项目是OutputStream::try_default(),你如何处理它并不重要Sink::try_new(&handle),它的行为总是相同的,而不是这样try_default,如果你匹配或者if let它会工作得很好,如果你unwrap这样做它就会失败。
但为什么会这样呢,两者应该是等价的。答案就在 rodio 的细节中,特别是OutputStreamHandle:
pub struct OutputStreamHandle {
mixer: Weak<DynamicMixerController<f32>>,
}
Run Code Online (Sandbox Code Playgroud)
因此,OutputStreamHandle(此后的 OSH)仅保留对 a 的弱引用DynamicMixerController,OutputStream(此后的 OS)对其持有强引用。
这意味着只有操作系统处于活动状态,OSH 才能“工作”。
let (_, handle) = OutputStream::try_default().unwrap();
Run Code Online (Sandbox Code Playgroud)
没有命名操作系统,因此不保留它,它立即被丢弃,被Arc释放,OSH 不保留任何内容,并且接收器不高兴。
那么另外一个如何工作呢?因为
if let Ok((_, handle)) = OutputStream::try_default() {
let sink = Sink::try_new(&handle).unwrap();
println!("match {}", sink.len());
}
Run Code Online (Sandbox Code Playgroud)
确实是
{
let _var = OutputStream::try_default();
if let Ok((_, handle)) = _var {
let sink = Sink::try_new(&handle).unwrap();
println!("match {}", sink.len());
}
}
Run Code Online (Sandbox Code Playgroud)
所以match/if let本身正在保持Result活力,这在这里是一件幸事(但会在下一个问题中引起问题)。
由于Result保持活动,元组保持活动,OutputStream保持活动,Arc保持活动,因此 OSH 具有工作mixer,接收器可以使用它。
OutputStream您可以通过将 绑定到“正确的”名称来解决第二个版本的问题,例如
let (_stream, handle) = OutputStream::try_default().unwrap();
Run Code Online (Sandbox Code Playgroud)
为名称添加前缀_将创建真正的绑定,但会抑制“未使用的变量”警告。
FWIW 小心处理这类事情对于 RAII 类型非常重要,这些类型的值是您“不需要”的,最常见的是互斥体保护代码而不是数据:
let _ = m.lock().unwrap();
Run Code Online (Sandbox Code Playgroud)
不执行任何操作,互斥量防护不会保持活动状态,因此锁会立即释放。在这种情况下,你宁愿
let _lock = m.lock().unwrap():
Run Code Online (Sandbox Code Playgroud)
甚至更好
let lock = m.lock().unwrap();
...
drop(lock);
Run Code Online (Sandbox Code Playgroud)