Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 215
Newbyte Avatar asked Nov 23 '19 23:11

Newbyte


People also ask

Is semi colon required in Rust?

They're not optional. Semicolons modify the behaviour of an expression statement so it should be a conscious decision whether you use them or not for a line of code. Almost everything in Rust is an expression. An expression is something that returns a value.

Should you use return in Rust?

You have to use return if you want to produce a value other than the final value of the block. The official style, then, is to leave off the return in the trailing position because it's redundant.

Does return statement have semicolon?

All developers writing JavaScript should understand automatic semicolon insertion as it relates to return statements. JS uses automatic semicolon insertion. This means JS engines will execute code and insert any semicolons where it sees fit. One of those spots is after a return statement.

When to use return keyword in Rust?

The return keyword can be used to return a value inside a function's body. When this keyword isn't used, the last expression is implicitly considered to be the return value. If a function returns a value, its return type is specified in the signature using -> after the parentheses () .


1 Answers

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.

like image 95
Optimistic Peach Avatar answered Sep 22 '22 09:09

Optimistic Peach