我做了一个小程序,它显示了一个奇怪的行为,我无法解释。我正在使用rodio机箱尝试一些音频的东西。
我做了两个项目,在我看来,应该给出同样的结果。
第一个我使用匹配来处理错误:
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());在上一篇文章中,我用解包代替了。
let (_, handle) = rodio::OutputStream::try_default().unwrap();
let s = Sink::try_new(&handle).unwrap();
println!("{}",s.len());第一个按预期执行print语句,而最后一个则在第二个解包装中恐慌。
当没有错误传播、隐式转换或其他可以解释这一点的东西时,我就觉得很奇怪。这里的问题与错误本身无关,而与两种代码之间的差异有关。
发布于 2021-09-30 14:09:17
问题是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就只能“工作”。
let (_, handle) = OutputStream::try_default().unwrap();不命名操作系统,所以不保存它,它立即被丢弃,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的活力,这在这里是一件好事(但是causes issues just the next question over)。
由于Result保持存活,元组保持存活,OutputStream保持存活,Arc保持存活,因此OSH有一个工作mixer,接收器可以利用它。
您可以通过将OutputStream绑定到“适当”名称来解决第二个版本的问题。
let (_stream, handle) = OutputStream::try_default().unwrap();用_作为名称的前缀将为real创建绑定,但会抑制“未使用的变量”警告。
对于RAII类型的值“不需要”(最常见的是互斥保护代码而不是数据),FWIW在这类事情上小心是非常重要的:
let _ = m.lock().unwrap();不做任何事情,互斥保护没有保持活着,所以锁立即释放。在这种情况下,你宁愿
let _lock = m.lock().unwrap():甚至更好
let lock = m.lock().unwrap();
...
drop(lock);https://stackoverflow.com/questions/69393226
复制相似问题