如何在函数成功时返回无结果的错误?有什么惯用方法?

66

Rust中,我相信处理可恢复错误的惯用方法是使用Result。例如,这个函数显然是惯用的:

fn do_work() -> Result<u64, WorkError> {...}

当然,有些函数只有一个明显的失败状态,因此使用 Option 类型。一个惯用的例子是:

fn do_work() -> Option<u64>

这些问题在文档中都有明确的解答。然而,我对一个函数在失败时没有实际意义的返回值而成功时也没有有意义的返回值的情况感到困惑。请比较以下两个函数:

fn do_work() -> Option<WorkError>
// vs
fn do_work() -> Result<(), WorkError>

我不确定这两种方式哪种更符合惯用语,或在实际的Rust代码中使用更频繁。我通常寻找这类问题的资源是Rust书籍,但我认为在其"错误处理"部分中没有涉及到这个问题。我在其他任何Rust文档中也没有找到太多有用信息。

当然,这似乎很主观,但我正在寻找权威来源,无论是说明哪种形式符合惯用语,还是说明为什么一种形式优于(或劣于)另一种形式。(我还好奇这种约定与其他大量使用"错误作为值"的语言(如Go和Haskell)相比如何。)


2
我属于“Result<(), Error>”这一方面。通常我也会将其别名为自己的类型。我很想听听其他人的看法。我这样做是因为“try!”宏与之非常搭配。 - Simon Whitehead
2个回答

64

使用fn do_work() -> Result<(), WorkError>

Result<(), WorkError> 意味着您希望完成工作,但可能会失败。

Option<WorkError> 意味着您想要获取一个错误,但它可能不存在。

当您编写do_work()时,您可能希望完成工作但不希望出现错误,因此Result<(), WorkError>是更好的选择。

我期望Option<WorkError>只在类似fn get_last_work_error() -> Option<WorkError>的情况下使用。


@其他人 由于do_work本身就是一个概念性的例子,我不认为会有任何官方约定。但是你可以在标准库中了解到惯用语的含义,例如:https://doc.rust-lang.org/nightly/std/io/trait.Read.html#method.read_exact - WiSaGaN
7
@其他人,整个标准库在操作可能失败但不返回任何有用信息时(例如,std::io和std::fs的许多函数)普遍使用Result<(), _>。这是正确的选择。 - huon
@其他人,使用Result已被官方认可,参考 https://aturon.github.io/errors/signaling.html#obstructions (RFC 236)。 - ArtemGr
请注意,该页面讨论的替代方案在 OP 问题的上下文中将是 Option<()> 而不是 Option<WorkError>。前者中的 None 表示工作未正确完成,而后者中的 None 表示工作已正确完成。 - WiSaGaN
@bluss 是的,通常 Result<Value, Error>Option<Value> 之间的区别是显而易见的。很少有一些用法确实不那么明显。例如,对于非阻塞地从队列中获取项目,Option<Item> 在这里似乎是合理的,而 Result<Item, TryError> 并不罕见,它将 TryError 映射到类似于 POSIX 的 WOULDBLOCK - WiSaGaN
显示剩余2条评论

8
Rust是“相当强类型的”(请不要在我如何衡量一种语言的强类型程度上指责我...)。我的意思是,通常情况下,Rust会为您提供工具来让类型“代表”您并记录您的代码,因此使用此功能编写可读代码是惯用法。
换句话说,你正在问的问题应该更多地是“哪种类型最能代表函数对任何阅读其签名的人所做的事情?”
对于>,您可以直接从文档中看到:
Result是一个表示成功(Ok)或失败(Err)的类型。
所以,针对您的情况,这意味着如果函数成功,则返回空(由Ok<()>表示),如果有错误,则返回WorkError(由Err<WorkError>表示)。这在代码中是对您在问题中描述函数的方式的非常直接的表示。
Option<WorkError>Option<()>进行比较。
类型选项表示可选值:每个选项都是Some并包含一个值或者是None,不包含任何值。
在你的情况下,Option表示对读者说:“这个函数应该返回一个WorkError,但它可能什么也不返回”。你可以记录“什么也不返回”意味着函数实际上是成功的,但这在类型上并不明显。
Option<()>表示“这个函数可以不返回任何东西或没有有意义的返回值”,如果WorkError不包含其他信息(比如错误类型或错误消息),而且这只是一种表达“发生了错误”的方式,这是一个合理的说法。在这种情况下,简单的bool传递相同的信息……否则,Result让你返回与错误相关的更多信息。

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