结果错误时从函数返回默认值

Pri*_*six 2 error-handling rust

是否有类似于?快捷方式的东西,它在出现错误时不是从函数返回结果,而是返回预定义的值?

基本上我想知道是否可以在一行中执行以下操作:

fn index() -> String {
    let temp = some_func("pass"); // some_func returns a Result 
    if temp.is_err() {
        return "No parameters named pass".to_string();
    }
    try_decrypt_data(temp.unwrap())
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用unwrap_or_else(),但这只是返回闭包而不是外部函数。IE

try_decrypt_data(params.get("pass").unwrap_or_else(|| return "No parameters named pass".to_string(); )) // This doesn't work
Run Code Online (Sandbox Code Playgroud)

Luk*_*odt 5

这是一种可能,但通常不是个好主意,尤其是在没有你的榜样(我将解释后)。

您不能轻松地返回 aString并使?返回默认值,但您可以定义自己的字符串类型并std::ops::Try为其实现。注意Try还是不稳定!

让我们看看这是如何工作的:

// Just wrap a string
struct StringlyResult {
    s: String,
}

// Convenience conversion 
impl From<String> for StringlyResult {
    fn from(s: String) -> Self {
        Self { s }
    }
}

// The impl that allows us to use the ? operator
impl std::ops::Try for StringlyResult {
    type Ok = String;
    type Error = String;

    fn into_result(self) -> Result<Self::Ok, Self::Error> {
        if self.s == "No parameters named pass" {
            Err(self.s)
        } else {
            Ok(self.s)
        }
    }

    fn from_error(s: Self::Error) -> Self {
        if s != "No parameters named pass" {
            panic!("wat");
        }
        Self { s }
    }

    fn from_ok(s: Self::Ok) -> Self {
        if s == "No parameters named pass" {
            panic!("wat");
        }
        Self { s } 
    }
}
Run Code Online (Sandbox Code Playgroud)

有了它,我们可以index()像这样实现:

fn index() -> StringlyResult {
    let temp = some_func("pass")
        .map_err(|_| "No parameters named pass")?; 
    try_decrypt_data(&temp).into()
}
Run Code Online (Sandbox Code Playgroud)

操场上的完整代码

所以是的,该Try特性使用户能够使用?具有自己类型的运算符。


但是,如您的示例所示,这是一个糟糕的主意。您可能已经注意到我上面代码中的“ wat ”部分。问题是您的 OK 类型已经耗尽了整个类型(该类型的所有实例都是有效的 OK 实例)。

考虑一个函数get_file_size() -> u64。现在这个函数可能会失败(即它无法确定文件大小)。你不能仅仅返回0来表示发生了故障。函数的调用者将如何区分函数无法确定文件大小的环境和文件实际为 0 字节大的环境?

同样,您的函数的调用者将如何区分发生错误的情况和解密文本字面意思的情况"No parameters named pass"?来电者不能!这很糟糕。

请注意,在 Rust 中有类似的东西,虽然没有那么糟糕,但仍然不是真正惯用的:get_file_size() -> i64. 在这里,我们可以返回-1来表示失败。这不那么糟糕,因为-1永远不可能是有效的文件大小!(换句话说,并非您类型的所有实例都是有效的 OK 实例)。但是,在这种情况下,忘记检查错误仍然非常容易。这就是为什么在 Rust 中,我们总是想使用Result.


为了使错误处理更容易,请考虑使用cratefailure。有了它,您可以轻松地将字符串用作错误消息,而不会牺牲程序的类型安全性或完整性。例子:

use failure::{Error, ResultExt};

fn index() -> Result<String, Error> {
    let temp = some_func("pass")
        .context("No parameters named pass")?; 
    Ok(try_decrypt_data(&temp)?)
}
Run Code Online (Sandbox Code Playgroud)