Rust中用?替换unwrap()时编译器要求返回Result/Option的报错问题
你遇到的编译问题和&引用、被调用方法本身的返回值类型无关,编译器的提示实际指向的是:使用?运算符的代码所在的外层函数,自身返回值必须是Result或Option类型,才能承接?向上传播的错误。
?的本质是语法糖:当它作用在Result类型上时,如果值是Err(e),就会直接return Err(From::from(e))提前退出当前函数;如果是Ok(v)就取出v继续执行。如果你写这段代码的函数本身返回值是()(比如写在main函数、或者没有声明Result返回值的普通方法里),自然没有办法承接提前return的错误,就会报你看到的错。
方案1:修改外层函数返回值,用?传播错误
如果你希望把错误交给上层调用方处理,首先要给当前函数声明符合要求的Result返回值,比如:
// 假设这是你原来写逻辑的方法,原来没有返回值 fn process_buffer(&mut self, buf: &[u8], src: SrcAddr) -> Result<(), MyError> { let json = str::from_utf8(&buf)?; let msg: MyMessage = serde_json::from_str(json)?; self.tx.try_send((msg, src))?; Ok(()) }
这时候就会遇到你提到的错误类型不匹配问题:str::from_utf8抛出的错误是Utf8Error,serde_json::from_str抛出的是serde_json::Error,try_send也有自己的错误类型,这些错误没法直接统一成你定义的MyError,这时候确实需要From特征做类型转换,这是Rust错误处理里非常常规的操作,完全不是什么奇技淫巧:
- 写原型、快速验证逻辑的时候,最省事的方式是用
Box<dyn std::error::Error>作为错误类型,标准库已经为所有常见错误实现了到这个类型的From转换,不需要写额外代码,直接就能用:fn process_buffer(&mut self, buf: &[u8], src: SrcAddr) -> Result<(), Box<dyn std::error::Error>> { let json = str::from_utf8(&buf)?; let msg: MyMessage = serde_json::from_str(json)?; self.tx.try_send((msg, src))?; Ok(()) } - 正式项目里一般会自定义枚举类型作为项目统一错误类型,通常用
thiserror这类库自动生成对应的From实现,不需要手动写大量重复的trait impl代码,开发效率很高。
方案2:就地处理错误,不向上传播
如果你不想修改函数的返回值类型,就继续用你现在写的match匹配逻辑就可以,也可以用if let写法简化:
let json = match str::from_utf8(&buf) { Ok(v) => v, Err(e) => { // 这里写你的错误处理逻辑:打日志、统计错误、返回默认值都可以 return; } }; let msg = match serde_json::from_str(json) { Ok(v) => v, Err(e) => { // 同理处理JSON解析错误 return; } }; self.tx.try_send((msg, src)).expect("Could not send data");
这种写法不需要外层函数返回Result,和?的区别是错误不会向上传播,直接在当前函数内处理完毕。
不用教条地把所有unwrap()都替换成?:unwrap()的语义是“我确定这里不可能出错,出错了直接panic就可以”,在示例代码、原型开发、或者你能100%保证不会触发Err分支的场景下,unwrap()是完全合理的写法。?只适合你需要把错误交给上层调用方统一处理的场景。
内容的提问来源于stack exchange,提问作者Jammer

