:#/ GSLF Journal
← :#/ GSLF Journal

Rustlers Atom 1.7: Functions

On this page

<figure class="video" style="margin:0;">
<div style="position:relative;width:100%;padding-bottom:56.25%;height:0;overflow:hidden;">
<iframe src="https://www.youtube.com/embed/PgbYH45wuI?si=2aKVhZVUKF9HL33"
title="YouTube video player"
style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen></iframe>
</div>
</figure>

Functions in Rust are explicit and predictable. You define a function with fn, name it in snake_case` and declare parameters with type annotations:

fn main() {
    greet_to("Gioele");
}

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

Rust requires type annotations for all parameters — they’re not optional. This makes functions self-documenting and ensures compile-time clarity. A function body is a block expression, and, just like before, the last expression without a semicolon becomes the return value.

The Return Arrow (->)#

The arrow indicates the function’s output type. If a function returns nothing, it implicitly returns () (the unit type).

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

fn print_sum(a: i32, b: i32) {
    println!("Sum = {}", a + b); // returns `()`
}

You can also use the return keyword explicitly, especially for early exits or branching logic:

fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 {
        return None;
    }
    Some(a / b)
}

Functions can return tuples to bundle multiple values neatly:

fn split_sum(a: i32, b: i32) -> (i32, i32, i32) {
    let sum = a + b;
    (a, b, sum)
}

fn main() {
    let (x, y, total) = split_sum(10, 20);
    println!("{x} + {y} = {total}");
}

This is a common idiom for returning structured results without defining a full struct.

Parameters and Ownership#

Each parameter in a function is a binding with its own ownership semantics:

  • Passing a value like String moves ownership into the function.
  • Passing a reference (&T) borrows it temporarily.
  • Passing a mutable reference (&mut T) allows in-place modification.
fn main() {
    let mut s = String::from("Hello");
    append_world(&mut s);
    println!("{s}");
}

fn append_world(s: &mut String) {
    s.push_str(", world!");
}

The signature &mut String declares that the function can mutate the borrowed string, but it doesn’t own it. Once the function ends, the borrow ends too.