Rust调用消费值的出错API时,如何避免克隆保留原值?
处理消费值的Rust API错误映射问题
假设调用一个会消费值的Rust API,该API返回Result类型,可能返回错误。如果发生错误,需要从原始值中获取信息并返回给当前函数的调用者。
use thiserror::Error; #[derive(Debug, Error)] enum InnerError { #[error("Inner error occurred")] SomeInnerError, } #[derive(Debug, Error)] enum OuterError { #[error("Outer error occurred")] SomeOuterError(u32), } //#[derive(Clone)] 假设无法实现Clone struct MyStruct { // 结构复杂,假设不可克隆 x: u32, } fn foo(v: MyStruct) -> Result<(), InnerError> { // 消费v drop(v); Err(InnerError::SomeInnerError) } fn bar() -> Result<(), OuterError> { let value = MyStruct { x: 42 }; // 如果调用失败,需要从原始value中获取信息返回给上层调用者: foo(value) // 无法使用.clone() .map_err(|_| OuterError::SomeOuterError(value.x)) } fn main() -> Result<(), Box<dyn std::error::Error>> { bar()?; Ok(()) }
请注意:假设foo(v: MyStruct)是固定的,无法修改它的消费值特性(这是否是良好的API设计?或许本问题的答案能解答这点)。
当前代码无法编译,因为value的所有权已转移到foo()中,无法在map_err()闭包中非法使用。如果MyStruct可克隆,可提前克隆值,但无错误时克隆会浪费性能,且不确定编译器是否会优化掉该克隆操作。
但如果MyStruct不可克隆(例如其内部包含无法克隆的内容),该如何保留原始值用于错误映射?
已有思路分析
思路一:让InnerError携带原始值所有权
修改InnerError,让其变体携带MyStruct的所有权:
enum InnerError { SomeInnerError(MyStruct) }
这样就可以从错误中提取原始值,而非从本地作用域获取:
foo(value) .map_err(|e| OuterError::SomeOuterError(e.x)) // 从错误对象e中获取信息 }
但缺点是,如果InnerError有多个变体,就需要在map_err(...)闭包中编写繁琐的match语句,从每个变体中提取原始值:
foo(value) .map_err(|e| { match e { InnerError::SomeInnerError(inner_value) => OuterError::SomeOuterError(inner_value.x), InnerError::SomeOtherInnerError(inner_value) => OuterError::SomeOuterError(inner_value.x), InnerError::AnotherInnerError(inner_value) => OuterError::SomeOuterError(inner_value.x), InnerError::NotTheLastInnerError(inner_value) => OuterError::SomeOuterError(inner_value.x), // 以此类推... } } }
思路二:提前克隆所需字段
提前克隆value中构造OuterError所需的字段,但这种方式不够优雅,且如果MyStruct包含私有字段或需要调用方法获取信息时,该方案不可行。
疑问
- 对于可克隆的结构体,大多数开发者可能会直接接受额外的
.clone()操作,但如果结构体不可克隆,该如何处理? - 是否存在处理这种情况的惯用方法?
- 是否这类易出错的API绝不应该消费值,而应始终接受引用?
内容的提问来源于stack exchange,提问作者davidA
相关产品推荐
相关产品推荐

