Prochain Live dĂ©couverte TSSR et AIS : jeudi 10 octobre Ă  12h (je m’inscris)

🩀Qu’est-ce que l’ownership en Rust ?

2024-06-10
Ownership en rust

L’ownership en Rust est un des concepts fondamentaux du langage qui garantit la sĂ©curitĂ© de la mĂ©moire et prĂ©vient les problĂšmes de concurrence. En comprenant les rĂšgles de l’ownership et en utilisant les rĂ©fĂ©rences lorsque nĂ©cessaire, Rust nous permet d’écrire un code sĂ»r et efficace. Bien que cela puisse sembler complexe au dĂ©but, une fois que l’on maĂźtrise les concepts de base, on peut se dĂ©tendre et apprĂ©cier coder en Rust 🩀

La gestion de la mémoire

Avant de parler de Rust, il me semble important de revenir sur le concept de gestion de la mĂ©moire dans un ordinateur. Venant du dĂ©veloppement web, je n’avais pas vraiment de notions sur la gestion de la mĂ©moire, ou du moins c’était assez enfoui. Logique, vu que la majoritĂ© des langages web ont Ă©tĂ© conçus pour gĂ©rer la mĂ©moire Ă  la place du dĂ©veloppeur. Lorsque je me suis lancĂ© dans Rust, j’ai dĂ» revenir sur des concepts de computer science, dont le fonctionnement de la mĂ©moire. Pour m’aider, comme toujours, le Book Rust ! En effet, dans Ă  peu prĂšs tous les chapitres, il y a des explications complĂ©mentaires laissĂ©es pour ceux qui auraient quelques lacunes en computer science. Prenez donc le temps de lire ce contenu sur la gestion de la mĂ©moire avant de passer Ă  la suite 😌.

Si vous connaissez dĂ©jĂ  le fonctionnement de la mĂ©moire, notamment parce que vous venez de langages tels que C / C++, et bien que cela nĂ©cessite de comprendre un nouveau concept, vous devriez apprĂ©cier de voir que Rust peut vous permettre de vous guider dans la gestion de la mĂ©moire. Et si vous me dites que vous perdez en libertĂ©, sachez que le concept de pointeur existe aussi dans Rust, et que vous pouvez aussi faire taire complĂštement les alertes de Rust Ă  la compilation grĂące Ă  unsafe (pas recommandĂ© Ă©videmment 😄).

C’est quoi l’ownership en Rust ?

En Rust, chaque variable est propriĂ©taire d’une valeur. Il ne peut y avoir qu’un seul propriĂ©taire Ă  la fois pour cette valeur. Lorsque le propriĂ©taire sort de la portĂ©e, la valeur est libĂ©rĂ©e.

Commençons par un exemple trÚs simple :

				
					fn main() {
	// On déclare une variable qui détient la chaßne de caractÚres "hello"
    let message = String::from("hello");
    println!("{}", s);
} // la variable 'message' sort de la portée et est libérée ici

				
			

Dans cet exemple, message est une chaßne (String) nouvellement créée. Lorsque message sort de la portée à la fin de la fonction main(), la mémoire allouée pour la chaßne est libérée automatiquement.

Voici un exemple un peu plus complexe :

				
					fn main() {
    let message = String::from("hello");
    let len = calculate_length(message); // ici, la fonction prend possession de `message`
    println!("La longueur de '{}' est {}.", message, len);
    // On obtient une erreur car `message` a été libéré auparavant
}

fn calculate_length(s: String) -> usize {
    s.len()
} 

// error[E0382]: borrow of moved value: `message`
//  --> src/main.rs:4:45
//   |
// 2 |     let message = String::from("hello");
//   |         ------- move occurs because `message` has type `String`, which does not implement the `Copy` trait
// 3 |     let len = calculate_length(message); // ici, `len` prend possession de `message`
//   |                                ------- value moved here
// 4 |     println!("La longueur de '{}' est {}.", message, len);
//   |                                             ^^^^^^^ value borrowed here after move
				
			

Dans cet exemple, on souhaite calculer la longueur d’une chaĂźne de caractĂšres Ă  l’aide de la fonction calculate_length. Pour cela on dĂ©clare une variable message puis on passe cette variable dans la fonction calculate_length. Lorsque la variable passe dans la fonction, elle est libĂ©rĂ©e et n’est donc plus utilisable dans le programme. C’est pour cela que l’on obtient une erreur lorsqu’on essaye de faire appel Ă  la variable message juste aprĂšs.

En Rust, c’est ce qu’on appelle le transfert de propriĂ©tĂ© (ownership transfer). Le transfert de propriĂ©tĂ© est le mĂ©canisme de base pour gĂ©rer les donnĂ©es. Lorsqu’une valeur est assignĂ©e Ă  une autre variable, elle est transfĂ©rĂ©e de la variable source Ă  la variable cible. Cela signifie que la variable source n’a plus accĂšs Ă  la valeur et la variable cible devient le nouveau propriĂ©taire.

Dans l’exemple ci-dessus toujours, Ă  la fin, je vous ai collĂ© le retour d’erreur quand on essaye de compiler le programme. Ici, l’erreur nous indique simplement qu’on essaye d’emprunter une variable qui a “bougĂ©â€ (moved). En effet, si vous avez bien compris, la variable message a effectivement “bougĂ©â€ lorsqu’elle est “partie” dans la fonction calculate_length. Cela confirme une nouvelle fois que la variable n’est plus utilisable en l’état.

				
					fn main() {
    let x = 5;
    let y = x; // x est copié, pas transféré
    println!("x: {}, y: {}", x, y); // x et y sont tous les deux valides
}
				
			

Dans cet exemple, x est copié dans y, donc les deux variables restent valides.

“Emprunter” une donnĂ©e : les rĂ©fĂ©rences

Pour partager des donnĂ©es sans transfert de propriĂ©tĂ©, Rust utilise des rĂ©fĂ©rences. Une rĂ©fĂ©rence permet Ă  une variable de rĂ©fĂ©rencer la valeur d’une autre variable sans en devenir le propriĂ©taire.

Reprenons l’exemple prĂ©cĂ©dent qui produisait une erreur, et corrigeons-le :

				
					fn main() {
    let message = String::from("hello");
    let len = calculate_length(&message); // ici avec le symbole "&" on indique le souhait d'emprunter la valeur
    println!("La longueur de '{}' est {}.", message, len);
}

fn calculate_length(s: &String) -> usize {
    s.len() // pas de transfert de propriété, s est une référence à la chaßne
}
				
			

Dans cette fonction, calculate_length prend une rĂ©fĂ©rence (&String) en argument, ce qui signifie qu’elle emprunte la chaĂźne plutĂŽt que de la possĂ©der. Ainsi, la chaĂźne reste valide mĂȘme aprĂšs l’appel de la fonction.

 

Donc cette histoire d’ownership en Rust, c’est juste une histoire de propriĂ©tĂ© ! On doit essayer de se souvenir de ce que possĂšde chaque variable au fil de notre code, et puis si on se trompe, il y a toujours une remontĂ©e d’erreur qui permet de rĂ©gler le problĂšme ! J’espĂšre que tout cela semble un peu moins insurmontable, et que cela va vous donner envie de coder en Rust 🩀 ! Pour aller plus loin, je vous ai aussi rĂ©alisĂ© une sĂ©lection de ressources en français pour apprendre le Rust et surtout, il y a notre belle formation Rust, accessible si vous avez des bases de programmation orientĂ©e objet.  

Â