问题
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