You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中非返回Result场景下?操作符的合理使用方法问询

如何在非Result返回的函数中处理Result时避免过度缩进?

我非常认可Rust的类型系统,但处理Result类型时,很容易遇到过度缩进的问题。通常用map_err配合?操作符能解决,但这个方案只适用于函数返回Result的场景。如果函数错误处理完后不需要返回Result,问题就来了。

比如下面这段代码就无法运行:

/// Returns an HTML string with the published blogs first then the unpublished ones
fn edit_admin_page_filled() -> String {
    use database::blog_posts::*;
    // error_to_page returns an Html string from an error 
    let published_blog_posts: Vec<_> = get_published_blog_posts().map_err(error_to_page)?;
    let unpublished_blogposts = get_unpublished_blog_posts().map_err(error_to_page)?;

    admin_edit(published_blog_posts, unpublished_blogposts)
}

原因很明显:函数返回的是String,但?会尝试返回Err(String),类型不匹配。unwrap_or_else也解决不了这个问题。

我试过用闭包来避免缩进,虽然能正常编译,但写法太繁琐:

(|| -> Result<String, String> {
    let published = get_published_blog_posts().map_err(error_to_page)?;
    let unpublished = get_unpublished_blog_posts().map_err(error_to_page)?;
    Ok(admin_edit(published, unpublished))
})().unwrap_or_else(|e| e)

另外还有两个备选方案,但都有缺陷:

  • 用if_chain这类crate或者if let chains:只能处理Ok分支,没法通过类型系统优雅处理else分支;
  • 单独检查错误后用unwrap():可行但必须用unwrap(),除非能百分百保证不会出现Err,否则我不想用这个方法:
let published_blog_posts = get_published_blog_posts().map_err(error_to_page);
let unpublished_blogposts = get_unpublished_blog_posts().map_err(error_to_page);

if let Err(e) = published_blog_posts {
    return e;
}
if let Err(e) = unpublished_blogposts {
    return e;
}
admin_edit(published_blog_posts.unwrap(), unpublished_blogposts.unwrap())

推荐解决方案

1. 自定义宏(最简洁的稳定方案)

写一个简单的宏,模拟?的逻辑,但直接在错误时返回函数的目标类型:

macro_rules! try_return {
    ($expr:expr) => {
        match $expr {
            Ok(val) => val,
            Err(e) => return e,
        }
    };
}

然后在函数里直接用这个宏替代?:

fn edit_admin_page_filled() -> String {
    use database::blog_posts::*;
    let published = try_return!(get_published_blog_posts().map_err(error_to_page));
    let unpublished = try_return!(get_unpublished_blog_posts().map_err(error_to_page));
    admin_edit(published, unpublished)
}

这个宏会自动将Result拆包:如果是Ok就取出值继续执行,如果是Err就直接返回错误值,完全没有多余缩进,也不用依赖任何外部crate。

2. Nightly版的Try Blocks(不稳定)

如果你能用Rust Nightly版本,可以用Try Blocks特性,它允许在非Result返回的函数里直接用?,写法比闭包简洁:

#![feature(try_blocks)]

fn edit_admin_page_filled() -> String {
    use database::blog_posts::*;
    let result: Result<String, String> = try {
        let published = get_published_blog_posts().map_err(error_to_page)?;
        let unpublished = get_unpublished_blog_posts().map_err(error_to_page)?;
        admin_edit(published, unpublished)
    };
    result.unwrap_or_else(|e| e)
}

不过这个特性目前还未稳定,不适合生产环境使用。

总结

自定义宏是目前稳定版Rust中最优雅的解决方案,既保留了?操作符的简洁性,又能在非Result返回的函数中处理错误,完全避免过度缩进,也不用被迫使用unwrap()。

内容的提问来源于stack exchange,提问作者Kosmas Xenakis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 16:20:53