【问题标题】:What's the difference between using the return statement and omitting the semicolon in Rust?在 Rust 中使用 return 语句和省略分号有什么区别?
【发布时间】:2019-11-23 23:52:36
【问题描述】:

我正在编写一个函数,它在成功(和失败)时返回一个 serde_json::Value。以前在 Rust 中,我一直省略分号以从函数返回数据,如下面的代码示例所示:

use serde_json::{Result, Value};
use core::result::Result as ResultCore;

fn returning_function() -> ResultCore<Value, Value> {
    let data = r#"
    {
        "status": "ok",
        "response": {
            "data": "secret message!"
         }
    }
    "#;

    match str_to_json(data) {
        Ok(json_data) => match json_data["status"].as_str() {
            Some(status_str) => {
                if status_str == "ok" {
                    Ok(json_data["response"].clone())
                }
            }
            None => eprintln!("\"status\" was not a string")
        }
        Err(error) => eprintln!("something went wrong! here's what: {}", error)
    }

    Err(serde_json::Value::Null)
}

fn str_to_json(json_data: &str) -> Result<Value> {
    Ok(serde_json::from_str(json_data)?)
}

这是我不明白的部分:这无法编译。 Rust 的编译器告诉我“类型不匹配”,它是 expected type (),但 found type serde_json::value::Value。现在,我找到了一个可以编译的解决方案,如下所示:

use serde_json::{Result, Value};
use core::result::Result as ResultCore;

fn returning_function() -> ResultCore<Value, Value> {
    let data = r#"
    {
        "status": "ok",
        "response": {
            "data": "secret message!"
         }
    }
    "#;

    match str_to_json(data) {
        Ok(json_data) => match json_data["status"].as_str() {
            Some(status_str) => {
                if status_str == "ok" {
                    return Ok(json_data["response"].clone());
                    // ^ added return statement here
                }
            }
            None => eprintln!("\"status\" was not a string")
        }
        Err(error) => eprintln!("something went wrong! here's what: {}", error)
    }

    Err(serde_json::Value::Null)
}

fn str_to_json(json_data: &str) -> Result<Value> {
    Ok(serde_json::from_str(json_data)?)
}

通过添加return 语句,编译器突然很高兴,编译器再也没有什么可说的了。为什么是这样?我的印象是省略分号和使用 return 语句具有相同的含义——为什么它在这里不同?

【问题讨论】:

  • return 从整个函数返回,而块的最后一个表达式(如果省略分号)隐式返回 仅从该块。有关更多讨论,请参阅What type is the “type ()” in Rust? 的答案。

标签: rust return serde-json


【解决方案1】:

return 语句,也称为提前返回,将从最后一个/最里面的类函数作用域返回一个对象。 (类似函数,因为它适用于闭包和函数)

let x = || {
    return 0;
    println!("This will never happen!");
};
fn foo() {
    return 0;
    println!("This won't happen either");
}

一个不存在的分号将改为评估表达式,如 return,但只返回到最后/最内层的范围,或者换句话说,它从 {} 的任何集合中返回。

let x = {           // Scope start
    0
};                  // Scope end

fn foo() -> usize { // Scope start
    0
}                   // Scope end

return 语句将突破任意数量的嵌套作用域,直到它到达类似函数的作用域:

fn foo() -> usize {// <------------------------------------------\
    {                                                      //    |
        {                                                  //    |
            {                                              //    |
                {                                          //    |
                    {                                      //    |
                        {                                  //    |
                            {                              //    |
                                {                          //    |
                                    {                      //    |
                                        {                  //    |
                                            {              //    |
                                                 return 0; // ---/
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

return 语句也有自己的类型,也就是说let x = return; 会真正编译。

return 语句将评估为 !,AKA the never type。你现在不能把它命名为稳定的 rust,但它最终会变得稳定和可用。

【讨论】:

    【解决方案2】:

    正如The Book中所说:

    在 Rust 中,函数的返回值与函数体块中的最终表达式的值同义。

    换句话说 - 不是一个表达式没有分号使它成为返回值,而是它是函数中的最终表达式。分号用于分隔表达式,因此:

    fn foo() -> i32 {
        5;
    }
    

    等效于产生值 5 的表达式,后跟一个不产生任何值的空表达式。因此上面的函数无法编译。

    return 关键字派上用场的地方是,如果您想在到达最终表达式之前尽早从函数返回。这就是您在示例中尝试执行的操作。

    还要注意,所有可能的返回值都必须与函数本身的返回值具有相同的类型。

    以上都不能完全解释您遇到的编译器错误。你的内心匹配是这样的:

    match json_data["status"].as_str() {
        Some(status_str) => {
            if status_str == "ok" {
                Ok(json_data["response"].clone())
            }
        }
        None => eprintln!("\"status\" was not a string")
    }
    

    匹配块的规则之一是每个臂必须评估为相同的类型。但在上面的例子中,一只手臂可能评估为std::result::Result&lt;serde_json::value::Value, _&gt;,而另一只手臂不评估任何东西(或者更准确地说,评估为空值())。

    插入return 可以避免该错误,因为Some 手臂现在完全从函数返回,而不是评估为std::result::Result&lt;serde_json::value::Value, _&gt; 类型的值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-24
      • 2012-12-20
      • 2020-09-09
      • 2011-08-26
      • 1970-01-01
      • 2015-11-04
      相关资源
      最近更新 更多