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);
我做了一个小程序,它显示了一个我无法解释的奇怪行为。 我正在使用 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);