Rust's functions look similar to other languages, but there are two things to keep in mind. (1) Specify both parameters and return types, and (2) the last expression of the block is the return value. Once you learn these two rules, Rust's expression-oriented style becomes natural.
Learning Objectives
- Define the function with the
fnkeyword and specify the parameters and return types. - Understand the difference between “expression” and “statement” and return a value without
return. - Write the code briefly by taking advantage of the fact that the block
{ ... }itself is an expression. - Write a
///documentation comment and create a document withcargo doc.
Key concepts
fn name (parameters: type, ...) -> return type {
//body
}
-
Parameter type is required.
-
Can be omitted if the return type is
()(unit). -
The function name and parameter name are
snake_case. -
statement is a code without value. Ends with a semicolon like
let x = 5;. -
expression is a code with a value.
5 + 3,if a { 1 } else { 2 }, block{ ... }. -
If you remove the semicolon from the last line of the function, it becomes expression → return value.
fn double(x: i32) -> i32 {
x * 2 //No semicolon → Return value
}
If you add a semicolon, it becomes () and a compilation error occurs due to return type mismatch.
If early termination is required, also use an explicit return. However, explicit return at the end of the function is not commonly used idiomatically.
If you write a comment starting with /// above a function, structure, or module, cargo doc will generate an HTML document. This is how all Rust standard documentation is created.
/// Returns the sum of two integers.
fn sum(a: i32, b: i32) -> i32 { a + b }
With cargo doc --open you can see the results right in your browser.
See as example
//Part 5 Example 1: Function definition/call, parameters and return value
//The type must be specified in the parameter.
//`-> i32` is the return type.
fn add(a: i32, b: i32) -> i32 {
a + b //No semicolon → expression → return value
}
//The return type of a function without a return value is unit `()`. Can be omitted.
fn greet(name: &str) {
println!("Hello, {name}!");
}
fn main() {
let sum = add(3, 4);
println!("3 + 4 = {sum}");
greet("Rust");
//You can call other functions within a function.
let total = add(add(1, 2), add(3, 4));
println!("total = {total}");
}
//Part 5 Example 2: Expression vs statement
//Key: If there is a semicolon, it is a "statement", if there is a semicolon, it is an "expression".
fn double(x: i32) -> i32 {
let y = x + x; //`let ...;` is a statement (no value)
y //There is no semicolon in the last line, so expression = return value
}
//Block `{ ... }` is also an expression. The value of the last expression becomes the value of the block.
fn classify(score: i32) -> &'static str {
let result = {
if score >= 60 {
"Pass"
} else {
"Retest"
}
};
result
}
//Explicit early return is also possible
fn abs(x: i32) -> i32 {
if x < 0 {
return -x;
}
x
}
fn main() {
println!("double(7) = {}", double(7));
println!("classify(55) = {}", classify(55));
println!("classify(75) = {}", classify(75));
println!("abs(-9) = {}", abs(-9));
println!("abs(9) = {}", abs(9));
}
//Part 5 Example 3: Documentation Comments (///)
//You can create an HTML document with `cargo doc` by writing `///` above the function.
/// Calculates the sum of two integers.
///
///
/// ```
//// // (The doctest example runs in the library crate.)
/// let s = 1 + 2;
/// assert_eq!(s, 3);
/// ```
fn sum(a: i32, b: i32) -> i32 {
a + b
}
/// Convert Celsius to Fahrenheit.
///
/// Formula: `°F = °C * 9 / 5 32`
fn celsius_to_fahrenheit(c: f64) -> f64 {
c * 9.0 / 5.0 + 32.0
}
fn main() {
println!("sum(2, 3) = {}", sum(2, 3));
println!("0°C -> {:.1}°F", celsius_to_fahrenheit(0.0));
println!("100°C -> {:.1}°F", celsius_to_fahrenheit(100.0));
println!();
println!("Document creation: You can check it in your browser with `cargo doc --open`.");
}
Common Mistakes
A. If you add a semicolon, the line becomes a “statement” and the value becomes (). The return type is i32, but when I try to return (), the type does not match. Remove the semicolon from the last expression or use return value;.
A. It is inferred from the body, but always specified in the function signature. Rust's design philosophy is to understand the intent of Caller/User by looking at the signature.
A. // is a general annotation, and /// is a documentation annotation, so cargo doc automatically collects it. Markdown can be used as is in ///, and it can also be executed as a doctest in ```code block iscargo test.
A. Rust does not support default arguments and name arguments at the language level. A similar effect can be achieved through the builder pattern, Option<T> parameter, or separate function overloading.
Cleanup
- The function signature is
fn name (parameter: type) -> return type. - If you remove the semicolon from the last line, the expression becomes the return value.
- Block
{ ... }itself is an expression, so it can be tied to a variable. - Automatically creates a document with
///documentation annotationcargo doc.
Try it yourself
Solve the assignment below. The reference solution is shown below.