What a function is
A function is a named block of code that performs one specific task. Instead of writing a long program as one list of instructions, you split it into small functions, each doing one thing, and give each a name. That keeps code readable, and it lets you run the same instructions many times without writing them again.
A function definition in Rust starts with the keyword fn:
fn say_hello() {
println!("Hello World");
}
It has two parts.
The header
- The keyword
fn, which starts every function definition. - The function's name. Rust style is
snake_case: lowercase words joined by underscores, likesay_hello. A name in camelCase still works, but the compiler warns about it. - Parentheses. They hold the function's parameters, which later lessons cover. For now they are empty.
- Optionally, a return type written after
->, such asfn add(a: i32, b: i32) -> i32. When you write no return type, the function returns the unit value(), which means "nothing useful".say_hellohas no return type, so it returns nothing.
The body
The body is the code that runs when the function is called. It is wrapped in curly braces { }. Here it holds one statement, println!("Hello World");. The ! after println marks it as a macro rather than an ordinary function, but you use it the same way: the text goes inside the parentheses, and the statement ends with a semicolon.
The indentation inside the braces is for readers. Rust does not depend on it, so braces are what tell the compiler where the body begins and ends.
Defining is not running
A definition only tells Rust that the function exists. If you put only the definition above in a program, nothing is printed. The body runs when the function is called, and you call a function by writing its name followed by parentheses and a semicolon:
fn say_hello() {
println!("Hello World");
}
say_hello(); // prints: Hello World
Each call runs the whole body once. Calling say_hello(); three times prints the message three times. You can call a function as often as you need.
In a complete standalone Rust program, code that runs belongs inside a function, and execution starts in a function called main. A complete version of the example looks like this:
fn say_hello() {
println!("Hello World");
}
fn main() {
say_hello();
say_hello();
}
This program prints Hello World twice. In the lesson's editor you write the definition and the call directly, as in the earlier example.
Order of definition
Functions defined at the top level of a Rust file can be called from code that comes before them in the file. Rust still reads best when a function is defined above the place where it is called, and the lesson follows that order.
The exercise, step by step
- Define the function. At the top of the file, write
fn say_hello()with empty parentheses, because it takes no parameters, followed by a pair of braces for the body. The name is exactlysay_hello, in snake_case. - Fill in the body. Inside the braces, use
println!to printHello everybody!. The text goes in double quotes, and the statement ends with a semicolon. - Call it. Below the definition, write
say_hello();. Without this line the program prints nothing, because the definition alone never runs.
fn say_hello() {
println!("Hello everybody!");
}
say_hello();
The output is the single line Hello everybody!.
Common mistakes
- Defining without calling. The function is correct, but nothing is printed.
- Forgetting the parentheses on the call. The call is
say_hello(), notsay_hello. The name by itself is not a call. - Forgetting the braces. A header with no braces after it is a compile error: the compiler expects
->,whereor{and finds the next token instead. - Leaving out the semicolon after
println!(...). This is allowed for the last statement in a body, but a semicolon is the habit to keep. - Calling an undefined or misspelled name. Calling
say_helo();gives the error "cannot find functionsay_heloin this scope". - Declaring a return type you don't produce. Writing
fn say_hello() -> i32with a body that only prints is a "mismatched types" error, because the body evaluates to(), not ani32.
Key takeaways
- A function groups statements under a name. It is defined with
fn name() { ... }. - Function names use snake_case. Without a
->type, a function returns nothing. - The body runs only when the function is called with
name();, and it runs fully on every call.
