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?
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.