在Rust中,如果main函数返回Err会发生什么?

26

根据The Rust Reference,

如果存在一个main函数,(snip),则其返回类型必须是以下之一:

  • ()

  • Result<(), E> where E: Error

但它没有说明当main()返回()Ok(())Err(<value>)时会发生什么。

就我测试的结果而言,

() Ok(()) Err(<value>)
退出状态 0 0 1
附加行为 - - Error: <value>被打印到stderr

这些行为是否在某些文档中明确定义、详细说明或保证?特别是,我可以假设

  • main()返回Err(<value>)时,程序总是以1状态退出吗?

  • main()返回Err(<value>)时,显示的错误消息始终是Error: <value>的形式吗?


注意事项:

  • 我希望有一种文件化的保证,而不是经验性的解释。这就是为什么我添加了#language-lawyer标签。

  • 这个问题不是关于何时应该使用()和何时应该使用Result<(), E>等等。正如你所知道的,可以在许多文档或教程中找到这些问题的答案(或至少是提示或标准)。


更新:

Termination 特性在 Rust 1.61.0 中终于稳定下来了(source)。


2
Termination 这个 trait 在这里扮演着一个重要的角色。不幸的是,它也没有记录这种行为。我的猜测是,除了那些 Termination for Result 的实现之外,它目前在任何地方都没有记录或保证。 - Lukas Kalbertodt
1
这是Termination特性的RFC - rodrigo
1
“终止”文档确实指出:“默认实现返回libc :: EXIT_SUCCESS以表示成功执行。在失败的情况下,将返回libc :: EXIT_FAILURE。”因此,这部分可能是可靠的(尽管不稳定的功能一样)。另一方面,错误打印似乎没有记录(虽然在“impl Termination for Result <...>”声明的“E:Debug”边界中暗示了)。 - Jmb
1
由于行为仅在不稳定的类型中记录(甚至没有完全记录,例如它委托给较低级别的 EXIT_SUCCESSEXIT_FAILURE 并未记录确切的输出),因此您不能假设其中任何一种情况,并且应该自己格式化输出并调用 std::process::exit()。当然,在实践中,行为极不可能改变,因为那将是一种不兼容的变化,但从语言专家的角度来看,您只是(尚)没有得到所要求的保证。 - user4815162342
2个回答

24
main 函数返回不同值的行为由 std::process::Termination 特质定义:

trait std::process::Termination

用于在主函数中实现任意返回类型的特质。

该特质被记录为在成功时返回 libc::EXIT_SUCCESS,在错误时返回 libc::EXIT_FAILURE

默认实现会返回 libc::EXIT_SUCCESS 以表示成功执行。在失败的情况下,返回 libc::EXIT_FAILURE

但是这些值在非 POSIX 系统上不能保证是0和1
至于打印错误消息,Termination 要求 E: Debug 并将 Debug 实现打印到 stderr 上,但我不认为它能保证完全相同。
impl<E: fmt::Debug> Termination for Result<!, E> {
    fn report(self) -> ExitCode {
        let Err(err) = self;
        eprintln!("Error: {:?}", err);
        ExitCode::FAILURE.report()
    }
}

源代码


8

这种行为由std::process::Termination Trait控制,该Trait是在RFC 1937中添加的。特别地,“隐藏”的lang_start()函数 - 调用main() - 大致如下:

fn lang_start<T: Termination>(
    main: fn() -> T,
    argc: isize,
    argv: *const *const u8
) -> !

即,main()可以返回任何T: Termination。在std中有Termination实现,包括!()std.process:ExitCode和一些Result<T, E>变量,其中E: Debug。这就是为什么你可以从main()返回()Ok(())等其他结果。
回答你的问题,语言律师模式:任何依赖于Termination的程序的确切行为并没有被语言本身严格规定。它是std实现的一部分,而不是语言参考的一部分。这意味着同一个程序在使用不同版本的编译器(绑定不同的std版本)编译时可能会有不同的行为。有关在Err案例中打印错误的确切行为已经被记录,但没有被指定。正如RFC 1937明确寻找类似于POSIX的行为一样,你可以合理地确信该程序不会以极其惊人的方式运行(例如,在Err案例中退出并具有状态0)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接