8.3 Parameters and Return Types

8.3.1 Specifying Parameter Types

Rust requires explicit types for parameters:

fn greet(name: &str) {
    println!("Hello, {}!", name);
}
  • If a function doesn’t explicitly return anything, it returns ().

8.3.2 Defining a Return Type

When your function should return a value, specify the type after ->:

fn get_five() -> i32 {
    5
}

8.3.3 The return Keyword and Implicit Returns

Rust supports both explicit and implicit returns:

Using return

#![allow(unused)]
fn main() {
fn square(x: i32) -> i32 {
    return x * x;
}
}

Implicit Return

In Rust, the last expression in the function body, if it ends without a semicolon, automatically becomes the return value:

#![allow(unused)]
fn main() {
fn square(x: i32) -> i32 {
    x * x  // last expression (no semicolon)
}
}
  • If you add a semicolon, the expression becomes a statement and no value is returned.

Comparison with C

In C, you must always use return value; to return a value.

8.3.4 Returning References (Including &mut)

In addition to returning values by ownership (e.g., String, i32, etc.), Rust lets you return references (including mutable references). For example:

fn first_element(slice: &mut [i32]) -> &mut i32 {
    // Returns a mutable reference to the first element in the slice
    &mut slice[0]
}

fn main() {
    let mut data = [10, 20, 30];
    let first = first_element(&mut data);
    *first = 999;
    println!("{:?}", data); // [999, 20, 30]
}

Key considerations:

  • Lifetime Validity: The referenced data must be valid for as long as the reference is used. Rust enforces this at compile time.

  • No References to Local Temporaries: You cannot return a reference to a local variable created inside the function, because it goes out of scope when the function ends.

    fn create_reference() -> &mut i32 {
        let mut x = 10;
        &mut x // ERROR: x does not live long enough
    }
  • Returning mutable references is perfectly valid when the data you reference is provided from outside the function (as a parameter) and remains alive after the function returns.

By carefully handling lifetimes, Rust ensures you do not accidentally return invalid references, preventing dangling-pointer issues common in lower-level languages.