What's the difference between using the return statement and omitting the semicolon in Rust?

泄露秘密 提交于 2020-03-24 18:13:52

问题


I'm writing a function that returns a serde_json::Value upon success (and failure). Previously in Rust I have been omitting the semicolon to return data from a function, like in the code example below:

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)?)
}

Here comes the part I don't understand: this doesn't compile. Rust's compiler tells me "mismatched types", and that it expected type (), but found type serde_json::value::Value. Now, I found a solution to this that does compile, and it is as follows:

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)?)
}

By adding the return statement the compiler suddenly is happy and the compiler doesn't have anything to say about it any more. Why is this? I was under the impression that omitting the semicolon and using the return statement had the same implications ­— why does it differ here?


回答1:


A return statement, otherwise known as an early return, will return an object from the last/innermost function-like scope. (Function-like because it applies to both closures and functions)

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

An absent semicolon will instead evaluate the expression, like a return, but only return to the last/innermost scope, or in other words, it returns from within any set of {}.

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

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

return statement will break out of any amount of nested scopes until it hits a function-like scope:

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

The return statement also has a type of its own, that is to say that let x = return; will actually compile.

A return statement will evaluate to !, AKA the never type. You can't name it in stable rust right now, but it will eventually become stable and usable.




回答2:


As it says in The Book:

In Rust, the return value of the function is synonymous with the value of the final expression in the block of the body of a function.

In other words - it is not the fact that an expression does not have a semicolon that makes it the return value, it is the fact that it is the final expression in the function. A semicolon is used to separate expressions, so this:

fn foo() -> i32 {
    5;
}

is equivalent to an expression yielding the value 5, followed by an empty expression that does not yield anything. Thus the function above would not compile.

Where the return keyword comes in handy is if you want to return from a function early, before reaching the final expression. This is what you are trying to do in your example.

Also note that all potential return values have to have the same type as the return value of the function itself.

None of the above fully explains the compiler error you were getting though. Your inner match looks like this:

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")
}

One of the rules of match blocks is that each of the arms has to evaluate to the same type. But in the case above, one arm potentially evaluates to std::result::Result<serde_json::value::Value, _>, while the other does not evaluate to anything (or to be more precise, evaluates to the empty value ()).

Inserting the return avoids that error, because the Some arm now returns from the function altogether, rather than evaluating to a value of type std::result::Result<serde_json::value::Value, _>.



来源:https://stackoverflow.com/questions/59013389/whats-the-difference-between-using-the-return-statement-and-omitting-the-semico

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!