Aller au contenu

Les fonctions en Rust

Une fonction est un bloc de code nommé et réutilisable. Au lieu de réécrire les mêmes étapes encore et encore, vous les placez une fois dans une fonction et vous l’appelez chaque fois que vous en avez besoin. Vous en utilisez déjà une : main.

On définit une fonction avec fn, et on l’appelle en écrivant son nom suivi de () :

fn greet() {
println!("Bonjour depuis une fonction !");
}
fn main() {
greet(); // on l'appelle
greet(); // et on l'appelle encore
}

Sortie :

Bonjour depuis une fonction !
Bonjour depuis une fonction !
▶ Essayez par vous-même

Les paramètres : donner des données à une fonction

Section intitulée « Les paramètres : donner des données à une fonction »

Une fonction devient vraiment utile quand on lui passe des données sur lesquelles travailler. Ces entrées s’appellent des paramètres. En Rust, vous devez écrire le type de chaque paramètre :

fn greet(name: &str) {
println!("Bonjour, {name} !");
}
fn main() {
greet("Alice");
greet("Bob");
}

Sortie :

Bonjour, Alice !
Bonjour, Bob !
▶ Essayez par vous-même

Vous pouvez avoir autant de paramètres que nécessaire, séparés par des virgules :

fn describe(name: &str, age: u32) {
println!("{name} a {age} ans");
}
fn main() {
describe("Ferris", 10);
}

Sortie :

Ferris a 10 ans
▶ Essayez par vous-même

Les valeurs que vous passez sont associées aux paramètres selon leur position : la première va dans name, la deuxième dans age. Si vous les passez dans le mauvais ordre, comme describe(10, "Ferris"), c’est une erreur de compilation, car les types ne correspondent pas.

Une fonction peut renvoyer une valeur à celui qui l’a appelée. On déclare le type de retour après une flèche -> :

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

Sortie :

3 + 4 = 7
▶ Essayez par vous-même

Remarquez que a + b, dans l’exemple ci-dessus, n’a pas de point-virgule. C’est une idée clé de Rust :

  • Une expression sans point-virgule est la valeur renvoyée.
  • Ajouter un point-virgule la transforme en instruction qui ne renvoie rien.

Ceci est donc incorrect :

fn add(a: i32, b: i32) -> i32 {
a + b; // ❌ le point-virgule veut dire « ne renvoie rien », mais on a promis un i32
}

Le compilateur va se plaindre que la fonction devrait renvoyer un i32 mais ne le fait pas. Il suffit d’enlever le point-virgule sur la dernière ligne.

fn square(n: i32) -> i32 {
n * n
}
fn main() {
let result = square(5);
println!("5 au carré vaut {result}");
}

Sortie :

5 au carré vaut 25
▶ Essayez par vous-même

Écrivez une fonction to_fahrenheit qui prend une température en degrés Celsius sous forme de f64 et la renvoie en degrés Fahrenheit. La formule est c * 9.0 / 5.0 + 32.0. Appelez-la pour 0, 100 et 37 degrés. Indice : la signature est fn to_fahrenheit(c: f64) -> f64, et le corps est une seule expression, sans point-virgule.

Voir la solution
fn to_fahrenheit(c: f64) -> f64 {
c * 9.0 / 5.0 + 32.0
}
fn main() {
println!("0 °C font {} °F", to_fahrenheit(0.0));
println!("100 °C font {} °F", to_fahrenheit(100.0));
println!("37 °C font {} °F", to_fahrenheit(37.0));
}

Sortie :

0 °C font 32 °F
100 °C font 212 °F
37 °C font 98.6 °F
▶ Essayez par vous-même

Remarquez qu’on passe 0.0, et pas 0. Le paramètre est un f64, et Rust ne transforme pas un entier en nombre à virgule sans vous le dire. Quand un f64 n’a pas de partie décimale, {} l’affiche sans le .0 : c’est pour ça que vous voyez 32 et non 32.0. Notez aussi que Rust affiche toujours un point comme séparateur décimal (98.6), jamais une virgule.

Écrivez une fonction largest(a: i32, b: i32, c: i32) -> i32 qui renvoie le plus grand de ses trois nombres. Testez-la avec (3, 9, 4) et (-2, -8, -5). Indice : if est une expression en Rust (voir If…else), donc une chaîne if / else if / else peut être la dernière ligne de la fonction.

Voir la solution
fn largest(a: i32, b: i32, c: i32) -> i32 {
if a >= b && a >= c {
a
} else if b >= c {
b
} else {
c
}
}
fn main() {
println!("{}", largest(3, 9, 4));
println!("{}", largest(-2, -8, -5));
}

Sortie :

9
-2
▶ Essayez par vous-même

Aucune des branches ne se termine par un point-virgule. Chacune est une expression, la chaîne if entière renvoie l’une d’elles, et c’est elle qui devient la valeur de retour. Utiliser >= plutôt que > garantit qu’en cas d’égalité, on renvoie quand même une valeur.

  • On définit une fonction avec fn et on l’appelle avec nom().
  • Les paramètres sont les entrées ; chacun a besoin d’un type : fn greet(name: &str).
  • On déclare un type de retour avec -> Type.
  • La dernière expression sans point-virgule est la valeur renvoyée.
  • return valeur; fonctionne aussi, mais on le réserve en général aux retours anticipés.

Petit quiz

1. En Rust, faut-il écrire le type de chaque paramètre d'une fonction ?

2. Comment déclare-t-on le type de retour d'une fonction ?

3. Pourquoi a + b (sans point-virgule) à la fin d'une fonction devient-il sa valeur de retour ?

4. Est-ce important de définir une fonction au-dessus ou en dessous de l'endroit où on l'appelle (par exemple, au-dessus ou en dessous de main) ?

Score : 0 / 4