Skip to main content
Rust Programming
CHAPTER 09 Beginner

Functions in Rust

Updated: May 18, 2026
5 min read

# CHAPTER 9

Functions in Rust

1. Chapter Introduction

If you write a 5,000-line application entirely inside fn main(), it will be unreadable and impossible to test. Functions allow you to break your code into small, modular, reusable blocks. They take inputs, perform logic, and optionally return outputs. In this chapter, we will learn how to declare functions, pass data into them, and return data back to the caller.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Declare basic functions.
  • Pass parameters into functions.
  • Understand why type signatures are mandatory for parameters.
  • Return values from functions.
  • Apply the concept of "implicit returns" using expressions.

3. Declaring a Basic Function

You define a function using the fn keyword, followed by the function name (in snake_case), parentheses (), and a block of code {}. Functions can be defined before or after main. Rust doesn't care about the order.
rust
123456789
fn main() {
    println!("Hello from main!");
    say_hello(); // Calling the function
}

// Declaring the function
fn say_hello() {
    println!("Hello from the custom function!");
}

4. Parameters and Arguments

You can pass data *into* a function. The variables in the definition are called Parameters. Rule: In function signatures, you MUST explicitly declare the type of each parameter. The compiler will not infer it.
rust
123456789
// 'user' is a string slice, 'age' is a u8 integer
fn greet_user(user: &str, age: u8) {
    println!("Welcome, {}! You are {} years old.", user, age);
}

fn main() {
    // "Alice" and 28 are the Arguments
    greet_user("Alice", 28); 
}

5. Returning Values

If a function performs a calculation, it can hand the result *back* to the code that called it. You must declare the return type using an arrow -> after the parameters.
rust
123456789
// This function promises to return an i32
fn multiply(x: i32, y: i32) -> i32 {
    return x * y;
}

fn main() {
    let result = multiply(5, 4);
    println!("Result is: {}", result); // Prints 20
}

6. Implicit Returns (The Rust Way)

Using the return keyword is perfectly valid, but it is not the "Rust Way". Because Rust is expression-based, the final expression in a block is automatically returned if you omit the semicolon!
rust
1234
// This is the idiomatic (standard) way to write Rust
fn add(x: i32, y: i32) -> i32 {
    x + y // Notice: No 'return' keyword, and NO SEMICOLON!
}

*If you accidentally add a semicolon (x + y;), the line becomes a statement, returns nothing (), and the compiler will throw an error because it expected an i32!*

7. Early Returns

If you need to return a value *before* the very last line (for example, inside an if statement to handle an error early), you must use the return keyword.
rust
12345678
fn divide(a: f64, b: f64) -> f64 {
    if b == 0.0 {
        println!("Error: Cannot divide by zero!");
        return 0.0; // Early return requires the keyword and a semicolon
    }
    
    a / b // Implicit return for the normal execution path
}

8. Common Mistakes

  • Forgetting Parameter Types: Writing fn calculate(a, b) instead of fn calculate(a: i32, b: i32). The compiler strictly requires type annotations in function signatures.
  • Accidental Semicolons on Returns: Writing x + y; at the end of a function that is supposed to return an i32. The compiler will say "expected i32, found (). Consider removing this semicolon."

9. Best Practices

  • Single Responsibility Principle: A function should do exactly ONE thing. If a function connects to a database, calculates user age, and prints to the screen, break it up into three smaller functions.
  • Use Implicit Returns: Drop the return keyword at the end of functions to write clean, idiomatic Rust.

10. Exercises

  1. 1. Write a function iseven(num: i32) -> bool.
  1. 2. Have it return true if the number is even, and false if odd. Use an implicit return!
  1. 3. Call it from main and print the result.

11. MCQs with Answers

Question 1

What keyword is used to declare a function in Rust?

Question 2

What is the standard naming convention for functions in Rust?

Q3. Does the Rust compiler infer the data types of function parameters? a) Yes b) No, you MUST explicitly declare them (e.g., x: i32) Answer: b) No, you MUST explicitly declare them.
Question 4

What syntax is used to declare the return type of a function?

Question 5

How do you return a value idiomatically at the end of a function in Rust?

Q6. What happens if you add a semicolon to the final implicit return line (e.g., x + y;)? a) It compiles normally b) It converts the expression into a statement, causing it to return () instead of the expected value, resulting in a compiler error. Answer: b) It returns () and causes a compiler error.
Question 7

When MUST you use the return keyword?

Q8. Does the order in which functions are defined in the file matter in Rust? a) Yes, you must define them before main b) No, Rust handles the order automatically Answer: b) No.
Question 9

If a function does not have a -> return type declared, what does it return by default?

Q10. Can functions be nested inside other functions in Rust? a) Yes b) No Answer: a) Yes.

12. Interview Questions

  • Q: Explain the difference between return x; and just writing x at the end of a function. Which is preferred in Rust?
  • Q: Why does Rust force you to write type signatures for parameters when it has type inference for regular variables? (Answer: Because function signatures act as a contract for the API, ensuring external code respects the rules without the compiler having to guess).

13. Summary

Functions break code down into manageable, reusable pieces. Rust requires strict type definitions for the input parameters and the output return value, ensuring robust communication between different parts of your application. By leveraging expressions, returning data at the end of a function becomes a clean, one-line process.

14. Next Chapter Recommendation

We have been using simple integers so far. But what happens when we use complex data like Strings? In the most important chapter of this course, Chapter 10: Ownership and Borrowing, we will unlock the secret to Rust's memory safety and understand why it doesn't need a Garbage Collector!

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·