Raw data, clear context.

[
[
[

]
]
]

A calculation becomes easier to reuse when it has a name, an input and a result. Rust functions declare parameter types inside parentheses and a return type after an arrow.[1] We will use one to add one to two different numbers.

Pass a value, keep the result

These examples were tested on Linux ARM64 with rustc 1.99.1 and Cargo 1.99.1. Create a fresh project, then open its functions folder in your editor and terminal.

Shell
cargo new --vcs none functions
Output
$ cargo new --vcs none functions
Creating binary (application) `functions` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
exit=0

Replace src/main.rs with this complete programme.

src/main.rs
Rust
fn add_one(value: i32) -> i32 {
value + 1
}
fn main() {
let first = add_one(4);
let second = add_one(9);
println!("First: {first}");
println!("Second: {second}");
}

In add_one, value is the parameter name and i32 is its type. The arrow followed by i32 declares the type of result.[1] Both calls supply a small integer; the function evaluates the same calculation for each input.

The final line, value + 1, is an expression. It produces a value, which becomes the function’s result.[1] Leave this line without a semicolon. The let statements in main store each returned value, then println! displays it.

Run the programme from the functions folder. Each terminal capture includes the executed command and its exit status.

Shell
cargo run --quiet
Output
$ cargo run --quiet
First: 5
Second: 10
exit=0

Our two calls returned 5 and 10. The calculation lives in one place, while each call supplies its own input. We use small i32 values here to keep the example focused on passing and returning values.

A function receives the integer 4 as parameter value, evaluates value plus 1, and returns 5 as i32. A separate comparison shows that adding a semicolon makes the body yield unit (), conflicting with the declared i32 result.
The first call passes 4 and receives 5. Without a semicolon, the final expression supplies the result; with one, this body yields () and fails the i32 return-type check.

A common error: one extra semicolon

Replace src/main.rs with this deliberately failing version. Only the calculation’s ending has changed.

src/main.rs
Rust
fn add_one(value: i32) -> i32 {
value + 1;
}
fn main() {
let first = add_one(4);
let second = add_one(9);
println!("First: {first}");
println!("Second: {second}");
}

Adding a semicolon turns that expression into a statement. This function body then yields the unit value, written (), although its signature promises i32.[1] Check the programme without building an executable.[3]

Shell
cargo check --quiet
Output
$ cargo check --quiet
error[E0308]: mismatched types
--> src/main.rs:1:27
|
1 | fn add_one(value: i32) -> i32 {
| ------- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | value + 1;
| - help: remove this semicolon to return this value
For more information about this error, try `rustc --explain E0308`.
error: could not compile `functions` (bin "functions") due to 1 previous error
exit=101

Our compiler reported E0308, expected i32, found (). E0308 means the expected and received types differ.[2] Remove the semicolon after value + 1 and restore the working programme.

Exercise

Rename the function add_two, update both calls, and change the calculation to value + 2. Run it again. Expect First: 6 and Second: 11.

Next: use if and else to choose a result based on a condition.

Browse Rust, one step at a time

Sources

  1. The Rust Book: Functions
  2. Rust error E0308: mismatched types
  3. The Rust Book: Hello, Cargo!

Leave a Reply