8.1 Defining and Calling Functions

8.1.1 Basic Function Definition

In Rust, functions are defined using the fn keyword, followed by the function name, an optional parameter list enclosed in parentheses (), and an optional return type specified after ->. The function body is a block of code enclosed in braces {}. The portion preceding the function body is often referred to as the function header or signature.

fn function_name(parameter1: Type1, parameter2: Type2) -> ReturnType {
    // Function body
}
  • Parameters: Each parameter must have a name and a type, separated by a colon :.
  • Return Type: Specified after the -> symbol. If omitted, the function returns the unit type (), similar to void in C.
  • Function Body: Contains the code to be executed when the function is called.

Function Position in Code

  • In Rust, the position of function definitions in the program text does not matter. You can call a function before its definition appears in the code.
  • There is no need for separate function declarations (prototypes) as in C. The Rust compiler reads the entire module before compilation, so it knows all function definitions.

Example:

fn main() {
    let result = add(5, 3);
    println!("Result: {}", result);
}

fn add(a: i32, b: i32) -> i32 {
    a + b
}
  • Here, add is called before it is defined, and the compiler has no issue with that.

Comparison with C

C Code:

#include <stdio.h>

int add(int a, int b); // Function declaration (prototype)

int main() {
    int result = add(5, 3);
    printf("Result: %d\n", result);
    return 0;
}

int add(int a, int b) { // Function definition
    return a + b;
}
  • In C, if you call a function before its definition, you must provide a function declaration (prototype) beforehand.
  • Rust does not require function declarations; functions are defined once with their full signature and body.

8.1.2 Calling Functions

You can call any function you've defined by using its name followed by parentheses. If the function accepts arguments, they are placed inside the parentheses, separated by commas. Arguments must be passed in the same order as specified in the function's parameter list. Within the function body, parameters are used just like regular variables.

Example:

fn main() {
    greet("Alice");
}
fn greet(name: &str) {
    println!("Hello, {}!", name);
}
  • The greet function is called with the argument "Alice".

Key Points

  • Function Name: The name of the function you want to call.
  • Parentheses: Always required, even if the function takes no arguments.
  • Arguments: Provided inside the parentheses, separated by commas.

8.1.3 Function Scope and Visibility

Rust doesn't enforce a specific location for function definitions, as long as they are visible to the caller.

  • Top-Level Functions: Functions defined at the module level are visible throughout the module and can be called from anywhere within it.
  • Nested Functions: Functions defined inside other functions (nested functions) are only visible within the enclosing function.

Example of Visibility:

fn main() {
    outer_function();
}

fn outer_function() {
    fn inner_function() {
        println!("This is the inner function.");
    }

    inner_function(); // This works
}

// inner_function(); // Error: not found in this scope
  • The inner_function is only visible within outer_function and cannot be called from main or elsewhere.