如何在Rust的Option.map()方法中使用?运算符?
Rust
?运算符闭包错误原因解析 问题背景
我是Rust新手,遇到如下编译错误:
the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
相关代码片段:
let player = Player::new( &input.id.unwrap_or_default(), input.team.map(|team| Team::new(&team.id)?), );
期望Team::new()出错时,Rust能返回Result的Err,但触发了上述错误。完整代码如下:
struct Team { id: String, } impl Team { fn new(id: &str) -> Result<Self, &'static str> { Ok(Self { id: id.to_string() }) } } struct PlayerCreate { id: Option<String>, team: Option<Team>, } struct Player { id: String, team: Option<Team>, } impl Player { fn new(id: &str, team: Option<Team>) -> Self { Self { id: id.to_string(), team, } } } fn main() { let input = PlayerCreate { id: Some("fake".to_string()), team: None, }; let player = Player::new( &input.id.unwrap_or_default(), input.team.map(|team| Team::new(&team.id)?), ); }
错误原因
map闭包的返回类型不兼容?的要求Option::map的闭包要求返回一个普通类型T,最终将Option<U>转换为Option<T>。但你在闭包里使用?时,?需要闭包的返回类型是Result/Option这类实现了FromResidual的类型——你这里闭包返回的是Result<Team, &'static str>,和map期望的Team类型不匹配,导致?没有合法的错误传播上下文。错误没有返回通道
你的Player::new返回的是Player而非Result<Player, ...>,就算?能正常工作,错误也没有地方可以返回。当前代码的整个调用链都没有设置错误传播的路径。
修正方案
要实现预期的错误传播逻辑,需要调整两处核心点:
方案1:使用transpose转换类型并提供错误返回通道
// 修改Player::new为返回Result,让错误有传播路径 impl Player { fn new(id: &str, team: Option<Team>) -> Result<Self, &'static str> { Ok(Self { id: id.to_string(), team, }) } } // 让main返回Result,允许错误向上传播 fn main() -> Result<(), &'static str> { let input = PlayerCreate { id: Some("fake".to_string()), team: Some(Team { id: "team1".to_string() }), }; // 用transpose将Option<Result<Team, E>>转为Result<Option<Team>, E>,?即可传播错误 let team = input.team.map(|team| Team::new(&team.id)).transpose()?; let player = Player::new(&input.id.unwrap_or_default(), team)?; Ok(()) }
方案2:用and_then处理Option内的Result转换
impl Player { fn new(id: &str, team: Option<Team>) -> Result<Self, &'static str> { Ok(Self { id: id.to_string(), team, }) } } fn main() -> Result<(), &'static str> { let input = PlayerCreate { id: Some("fake".to_string()), team: Some(Team { id: "team1".to_string() }), }; let player = Player::new( &input.id.unwrap_or_default(), // and_then允许闭包返回Option,配合ok()将Result转为Option input.team.and_then(|team| Team::new(&team.id).ok()), )?; Ok(()) }
内容的提问来源于stack exchange,提问作者Fred Hors
相关产品推荐
相关产品推荐

