Basic

Functions

Define reusable blocks of code that return values.

What you will see

50

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

You will define functions that return values, write implicit returns, and use closures to capture values.

1

I define a function that adds two numbers and call it. The return value flows into the variable.

fn add(a: i32, b: i32) -> i32 { a + b } fn main() { let result = add(3, 4); println!("{}", result); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
7
2

I return the last expression without a semicolon. Dropping the semicolon makes the expression the return value.

fn square(x: i32) -> i32 { x * x } fn main() { println!("{}", square(5)); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
25
3

I define a closure that adds two numbers and call it. Closures are anonymous functions stored in a variable.

fn main() { let add = |a, b| a + b; let total = add(2, 3); println!("{}", total); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
5
4

I use format! to build a String from a template and print it. The function returns the owned string.

fn greet(name: &str) -> String { format!("Hello, {}", name) } fn main() { println!("{}", greet("Ada")); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
Hello, Ada
5

I write a closure that captures a variable from its environment. The captured value is used inside the closure.

fn main() { let multiplier = 10; let f = |x| x * multiplier; println!("{}", f(5)); }

Before you run it, say out loud what it will print. Then open the answer.

What it prints
50

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

Arguments flow into a function; the last expression flows back as the return value.
1 Caller passes arguments
↓
2 Parameters receive the values
↓
3 The body computes an expression
↓
4 The value is returned to the caller
Why this works
Return types

Functions declare their return type:

fn greet(name: &str) -> String {
    format!("Hello, {name}")
}

String is the owned string type. format! builds it from a template. Functions return () when they produce no value.

Closures

Closures capture their environment:

let multiplier = 10;
let f = |x| x * multiplier;   // captures multiplier

Closures can capture variables by reference, mutable reference, or by ownership. Rust picks the least restrictive capture automatically.

Examples

A simple function

Defining functions

fn add(a: i32, b: i32) -> i32 {
    a + b
}

let result = add(3, 4);   // 7

fn add(a: i32, b: i32) -> i32 takes two i32 and returns one. The -> i32 declares the return type.

Why it works: the last expression in a block is the implicit return, no return keyword needed.

Returning values

Implicit returns

fn square(x: i32) -> i32 {
    x * x   // no semicolon = return value
}

Drop the semicolon to return the expression's value. Adding a ; turns it into a statement that returns ().

Watch out: a function that must return a value but ends with a ; returns () and fails to compile.

Closures

Anonymous functions

let add = |a, b| a + b;
let total = add(2, 3);

A closure is an anonymous function stored in a variable. Closures infer their parameter types from use. They capture surrounding variables.

Why it works: closures are values that can be passed around and called.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Loops
Previous Lesson
Loops
Next Lesson
Ownership Basics
Ownership Basics →