Skip to main content

Rust Functions Basics

Introduction

Functions are the building blocks of readable, maintainable, and reusable code. In Rust, functions play a crucial role in organizing code and implementing the language's powerful features. This guide will introduce you to the basics of Rust functions, from simple definitions to practical applications.

A function is a block of organized, reusable code that performs a specific task. By breaking your code into functions, you make it more modular, easier to debug, and simpler to maintain.

Defining Your First Function

Let's start with the simplest function definition in Rust:

rust
fn say_hello() {
println!("Hello, world!");
}

fn main() {
say_hello(); // Calling our function
}

Output:

Hello, world!

The syntax for defining a function in Rust is:

  • fn keyword to declare a function
  • Function name (using snake_case by convention)
  • Parentheses () for parameters (empty in this case)
  • Curly braces {} to enclose the function body

Function Parameters

Functions become more useful when they can accept inputs. In Rust, we call these inputs "parameters":

rust
fn greet(name: &str) {
println!("Hello, {}!", name);
}

fn main() {
greet("Alice");
greet("Bob");
}

Output:

Hello, Alice!
Hello, Bob!

Notice how we declared a parameter name of type &str (string slice). Rust requires explicit type annotations for all function parameters.

Multiple Parameters

You can define functions with multiple parameters:

rust
fn describe_person(name: &str, age: u32) {
println!("{} is {} years old.", name, age);
}

fn main() {
describe_person("Carol", 28);
describe_person("Dave", 35);
}

Output:

Carol is 28 years old.
Dave is 35 years old.

Return Values

Functions can also return values. In Rust, the return type is specified after an arrow ->:

rust
fn add(a: i32, b: i32) -> i32 {
a + b // Note: no semicolon here!
}

fn main() {
let sum = add(5, 3);
println!("The sum is: {}", sum);
}

Output:

The sum is: 8

Important Note About Returns

In Rust, the last expression in a function is implicitly returned if it doesn't end with a semicolon. This is why we wrote a + b without a semicolon in the add function.

If you prefer to be explicit, you can use the return keyword:

rust
fn add(a: i32, b: i32) -> i32 {
return a + b; // Explicit return with semicolon
}

Early Returns

Sometimes, you might want to return early from a function based on certain conditions:

rust
fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
return None; // Early return for division by zero
}

Some(a / b) // Normal return
}

fn main() {
match divide(10.0, 2.0) {
Some(result) => println!("10 ÷ 2 = {}", result),
None => println!("Cannot divide by zero!"),
}

match divide(10.0, 0.0) {
Some(result) => println!("10 ÷ 0 = {}", result),
None => println!("Cannot divide by zero!"),
}
}

Output:

10 ÷ 2 = 5
Cannot divide by zero!

This example demonstrates not only early returns but also the use of Rust's Option type for handling potential failure cases.

Functions as Expressions vs. Statements

In Rust, functions themselves are expressions, not statements. This means you can use them in ways that might surprise you if you're coming from other languages:

rust
fn get_greeting() -> &'static str {
"Hello, Rust programmer!"
}

fn main() {
let greeting = get_greeting();
println!("{}", greeting);
}

Output:

Hello, Rust programmer!

Function Scope and Lifetime

Variables defined inside a function are only accessible within that function:

rust
fn main() {
let outside_variable = 10;

{
// This is a new scope
let inside_variable = 20;
println!("Inside: can access outside_variable: {}", outside_variable);
println!("Inside: can access inside_variable: {}", inside_variable);
}

println!("Outside: can access outside_variable: {}", outside_variable);
// This would cause a compilation error:
// println!("Outside: cannot access inside_variable: {}", inside_variable);
}

Output:

Inside: can access outside_variable: 10
Inside: can access inside_variable: 20
Outside: can access outside_variable: 10

Practical Examples

Example 1: Temperature Converter

rust
fn celsius_to_fahrenheit(celsius: f64) -> f64 {
(celsius * 9.0 / 5.0) + 32.0
}

fn fahrenheit_to_celsius(fahrenheit: f64) -> f64 {
(fahrenheit - 32.0) * 5.0 / 9.0
}

fn main() {
let celsius = 25.0;
let fahrenheit = celsius_to_fahrenheit(celsius);
println!("{:.1}°C = {:.1}°F", celsius, fahrenheit);

let fahrenheit = 98.6;
let celsius = fahrenheit_to_celsius(fahrenheit);
println!("{:.1}°F = {:.1}°C", fahrenheit, celsius);
}

Output:

25.0°C = 77.0°F
98.6°F = 37.0°C

Example 2: Simple Calculator

rust
fn calculator(a: f64, b: f64, operation: char) -> Option<f64> {
match operation {
'+' => Some(a + b),
'-' => Some(a - b),
'*' => Some(a * b),
'/' => {
if b == 0.0 {
None // Cannot divide by zero
} else {
Some(a / b)
}
},
_ => None, // Invalid operation
}
}

fn main() {
let operations = [(5.0, 3.0, '+'), (10.0, 2.0, '/'), (7.0, 0.0, '/'), (4.0, 3.0, '^')];

for (a, b, op) in operations {
match calculator(a, b, op) {
Some(result) => println!("{} {} {} = {}", a, op, b, result),
None => println!("{} {} {} = Error", a, op, b),
}
}
}

Output:

5 + 3 = 8
10 / 2 = 5
7 / 0 = Error
4 ^ 3 = Error

Function Organization in Rust Programs

As your programs grow, organizing functions becomes important. Here's a diagram showing how functions are typically organized in Rust programs:

Summary

In this guide, we've covered the fundamentals of Rust functions:

  • Basic function syntax using the fn keyword
  • Parameters and type annotations
  • Return values and implicit returns
  • Early returns and conditional logic
  • Function scope and variable visibility
  • Practical examples showing functions in action

Functions are a fundamental building block in Rust, and mastering them is essential for writing clean, efficient, and maintainable code.

Exercises

  1. Write a function that calculates the area of a rectangle given its width and height.
  2. Create a function that takes a string and returns whether it's a palindrome (reads the same forward and backward).
  3. Implement a simple interest calculator function that takes principal amount, rate, and time as parameters.
  4. Write a function that accepts an array of integers and returns the sum, average, minimum, and maximum values.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)