我做了一个小程序,它显示了一个我无法解释的奇怪行为。
我正在使用 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());
第一个按预期执行打印语句,而最后一个在第二个解包中发生 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
(此后的操作系统)具有很强的引用值(value)。这意味着 OSH 仅在操作系统处于事件状态时“工作”。
let (_, handle) = OutputStream::try_default().unwrap();
没有命名操作系统,所以不要捕获它,它立即被删除,Arc
被释放,职业安全与卫生没有任何影响,水槽不高兴。那么另一个如何工作呢?因为
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
活着,这是一种祝福(但 causes issues just the next question over)。自
Result
保持事件状态,元组保持事件状态,OutputStream
保持活力,Arc
保持事件状态,因此 OSH 有一个可用的 mixer
,水槽可以使用。您可以通过绑定(bind)
OutputStream
来修复第二个版本的问题。到一个“正确的”名称,例如let (_stream, handle) = OutputStream::try_default().unwrap();
在名称前加上 _
将创建真正的绑定(bind),但会抑制“未使用的变量”警告。FWIW 小心处理这类事情对于 RAII 类型非常重要,这些类型的值您“不需要”,最常见的是互斥保护代码而不是数据:
let _ = m.lock().unwrap();
什么都不做,互斥锁没有保持事件状态,所以锁被立即释放。在这种情况下,你宁愿let _lock = m.lock().unwrap():
甚至更好let lock = m.lock().unwrap();
...
drop(lock);
关于rust - 匹配和解包之间的不同行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69393226/