Error handling 当结果为错误时从函数返回默认值

Error handling 当结果为错误时从函数返回默认值,error-handling,rust,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_dec

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

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

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())
}
我尝试使用
unwrap\u或\u else()
,但这只是返回闭包,而不是外部函数。i、 e

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

没有此类实用程序,但您始终可以编写宏:

macro_rules! return_if_err {
    ( $to_test:expr, $default:expr ) => (
        if $to_test.is_err() {
            return $default;
        }
    )
}

fn pop_or_default(mut v: Vec<i32>) -> i32 {
    let result = v.pop();
    return_if_err!(result.ok_or(()), 123);

    result.unwrap()
}

fn main() {
    assert_eq!(pop_or_default(vec![]), 123);
}
macro\u规则!如果出错,则返回{
($to_test:expr,$default:expr)=>(
如果$to_test.is_err(){
返回$default;
}
)
}
fn pop_或_默认值(mut v:Vec)->i32{
让结果=v.pop();
如果出错,则返回(result.ok或(()),123);
结果:展开()
}
fn main(){
assert_eq!(pop_或_default(vec![]),123);
}
您。

这是可能的,但通常不是一个好主意,尤其是在您的示例中(我将在后面解释)

您无法轻松返回
字符串
并使
返回默认值,但您可以定义自己的字符串类型并为其实现。请注意,
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 } 
    }
}
()

因此,是的,
Try
特性允许用户对自己的类型使用
操作符


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

考虑一个函数
get\u file\u size()->u64
。现在,此函数可能会失败(即,它无法确定文件大小)。您不能仅仅返回
0
来表示发生了故障。函数的调用方如何区分函数无法确定文件大小的环境和文件实际大小为0字节的环境

类似地,函数的调用者如何区分发生错误的情况和解密文本字面上是“没有名为pass的参数”的情况?打电话的人不能!这很糟糕

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


<>为了使错误处理更容易,请考虑使用。这样,您就可以轻松地将字符串用作错误消息,而不会牺牲程序的类型安全性或健全性。例如:

use failure::{Error, ResultExt};

fn index() -> Result<String, Error> {
    let temp = some_func("pass")
        .context("No parameters named pass")?; 
    Ok(try_decrypt_data(&temp)?)
}
使用失败:{Error,ResultExt};
fn index()->结果{
让温度=一些函数(“通过”)
.context(“没有名为pass的参数”)?;
确定(尝试解密数据(&temp)?)
}

我将创建一个使用
结果的内部函数。这允许您对要返回的各种错误消息/默认值使用问号运算符。然后可以调用内部函数并获取成功值或错误值:

fn index() -> String {
    fn inner() -> Result<String, String> {
        let t1 = some_func("pass").map_err(|_| "No parameters named pass")?;
        let t2 = some_func2(t1).map_err(|_| "A second error")?;
        let t3 = some_func3(t2).map_err(|_| "A third error")?;
        Ok(t3)
    }

    match inner() {
        Ok(v) => v,
        Err(v) => v,
    }
}

只是为了确保:
try\u decrypt\u data
将解密后的数据返回为
String
?如果发生错误,您希望返回字符串
“No parameters named pass”
,是吗?那么,如果解密的数据恰好是
“没有名为pass的参数”
,该怎么办?您如何判断是否发生了错误,或者解密的数据就是这样的?@Lukas Kalbertodt实际的try\u decrypt\u数据返回一个结果,然后我用
unwrap\u或(“…”).to\u string()
,但这与问题无关,因此我将其保留在这里以保持简单。谢谢,这回答了问题,但是似乎要减少2行代码需要做很多工作:D。在本例中,这只是一个个人项目,我知道解密输出会是什么样子,在未来,我想我将返回一个结果,并让包装器函数实际将字符串返回给客户机。将避免
展开
。如何编写这样的宏。
fn index() -> String {
    fn inner() -> Result<String, String> {
        let t1 = some_func("pass").map_err(|_| "No parameters named pass")?;
        let t2 = some_func2(t1).map_err(|_| "A second error")?;
        let t3 = some_func3(t2).map_err(|_| "A third error")?;
        Ok(t3)
    }

    match inner() {
        Ok(v) => v,
        Err(v) => v,
    }
}
#![feature(try_blocks)]

fn index() -> String {
    let v = try {
        let t1 = some_func("pass").map_err(|_| "No parameters named pass")?;
        let t2 = some_func2(t1).map_err(|_| "A second error")?;
        some_func3(t2).map_err(|_| "A third error")?
    };

    match v {
        Ok(v) => v,
        Err(v) => v,
    }
}