Aller au contenu

Les variables de types primitifs en Rust

Déclarer une variable

Rust est un langage à typage statique, ce qui veut dire qu’une fois déclarée, une variable ne peut pas changer de type.

En Rust on peut déclarer une variable sans lui donner de valeur, mais dans ce cas on est obligé de préciser son type.

La syntaxe est let ma_variable : mon_type;, un petit exemple :

fn main() {
let age: u8; // Comme aucune valeur on doit indiquer le type
}

Lorsqu’on déclare une variable et qu’on lui donne une valeur, on n’est pas toujours obligé d’indiquer son type, car le compilateur peut le deviner en fonction de la valeur de la variable.

fn main() {
let is_raining = true; // Ici rust devine que is_raining est un booléen
let is_raining: bool = true; // Là on indique manuellement que is_raining est de type bool
}

Dans certains cas on préfère indiquer le type d’une valeur manuellement.

fn main() {
let month_number = 12; // Dans ce cas si on ne précise rien,
// Rust utilisera le type i32 car c'est le type le plus large
// C'est un peu dommage car il n'y a que 12 mois
// alors on pourrait en utiliser 8bits au lieu de 32
let month_number: u8 = 12; // Ici on utilise un u8 explicitement
// car le mois est seulement positif et rentre dans 8bits.
}

Les variables sont immutables par défaut

La première chose à savoir avec les variables en Rust, c’est qu’elles sont immutables par défaut, c’est-à-dire qu’une fois créé, leur valeur ne peut pas être modifiée.

C’est un comportement qui peut paraitre bizarre, mais cela permet d’empêcher des effets de bords, c’est-à-dire des variables qui peuvent être modifié accidentellement en passant dans une fonction par exemple.

Là encore on voit bien l’importance que donne Rust à la rigueur.

Si on souhaite pouvoir modifier nos variables on va devoir les créer avec let mut (pour mutable)

fn main() {
let a = 13; // a ne pourra pas être modifié
let mut b = 12; // b pourra être modifié
}

Variable Shadowing

Comme on vient de le voir les variables sont immutables par défaut, en revanche on est autorisé à faire du variable shadowing, c’est-à-dire déclarer plusieurs fois une variable avec le même nom pour changer sa valeur.

fn main() {
let a = 1;
// La valeur et le type de a est changé
// grâce à du shadowing
let a = false;
}

À noter que même si c’est possible, c’est en général une mauvaise idée d’utiliser ce concept, si une variable doit être mutable il vaut mieux la créer directement mutable afin de rendre le code clair.

Les constantes

Bon là tu dois de te dire “Ouais en gros les variables sont des constantes par défaut vu qu’on ne peut pas les modifier ?”

Eh bien pas tout à fait puisqu’en Rust on a aussi les constantes, qui vont avoir quelques différences avec les variables.

  • Les constantes doivent avoir un nom en TOUT_EN_MAJUSCULE
  • Elles peuvent être dans le global scope
  • Leur type doit impérativement être défini explicitement même quand on indique la valeur
  • Une constante peut stocker uniquement une valeur dont la taille est connue à la compilation
const MA_CONSTANTE: u8 = 12;
fn main() {
println!("{}", MA_CONSTANTE);
}

Les Data types

Comme dans tous les langages on retrouve différents types de données que peut prendre une variable.

Le type bool (Booléen)

Le type booléen peut prendre soit la valeur true soit la valeur false, il est stocké sur 1bit en mémoire.

fn main() {
let is_day = true;
}

Le type charactère

  • Le type char (pour caractère) sert à contenir un seul caractère (lettre de l’alphabet, émoji, etc).
  • Il est compris entre guillemets simples obligatoirement, c’est ce qui permet de les différencier des strings
fn main() {
let c: char = 'z';
}

Une variable de type char est stockée sur 32bits et représente la valeur unicode du caractère, on peut donc stocker bien plus que de l’ASCII comme des accents, de l’alphabet chinois, des émojis, etc.

Les types composés

Les types composés peuvent grouper plusieurs valeurs dans 1 type. Rust possède 2 types primitifs composés : Les tuples et les arrays.

Le type tuple

  • Un tuple permet de stocker des valeurs qui peuvent être de type différents
  • Pour définir un tuple on utilise les parenthèses
  • Les tuples ont une taille fixée, une fois déclaré ils ne peuvent pas s’agrandir ou rétrécir
  • Les valeurs contenues dans un tuple peuvent changer si le tuple est mutable
fn main() {
let mon_tuple: (i32, f64, u8) = (500, 6.4, 1);
}

Dans l’exemple précédent on définit le type de tout ce qui se trouve dans le tuple, cette notation veut dire que le 1er élément est un i32, le 2ᵉ un f64 et le 3ᵉ un u8.

fn main() {
let mon_tuple: (i32, f64, u8) = (500, 6.4, 1);
}



Les tuples permettent de définir plusieurs variables sur une ligne grâce à la déstructuration des tuples, Rust va automatiquement assigner chaque variable du type de gauche avec la valeur correspondante du tuple de droite :

fn main() {
let (x, y, z) = (500, 6.4, 1);
println!("{}", x); // 500
println!("{}", y); // 6.4
println!("{}", z); // 1
}



On accède aux éléments d’un tuple en utilisant le ., par exemple si on veut le premier élément du tuple on fera mon_tuple.0 :

fn main() {
let mon_tuple = (500, 6.4, 1);
println!("{}", mon_tuple.0); // 500
println!("{}", mon_tuple.1); // 6.4
println!("{}", mon_tuple.2); // 1
}

Enfin un tuple sans aucune valeur est appelé tuple unitaire. Sa valeur et son type correspondant s’écrivent () et représentent une valeur vide ou un type de retour vide. Les expressions retournent implicitement () si elles ne retournent pas d’autres valeurs.

Le type array

Le type array a aussi une taille fixe, mais il ne peut stocker des valeurs que de même type !

Là encore on n’est pas obligé de préciser le type, mais si on le fait on doit impérativement préciser le nombre d’éléments contenu dans l’array.

Voici comment on définit explicitement le type d’un array

fn main() {
let mon_array: [i32; 5] = [1, 2, 3, 4, 5];
// i32 c'est le type des valeurs contenu dans l'array
// et 5 c'est le nombre d'élements dans l'array
}

Pour initialiser un array plus rapidement on peut utiliser la syntaxe suivante

fn main() {
let a = [3; 5];
// a vaut [3, 3, 3, 3, 3]
}

Ici la notation est au niveau de la valeur et pas du type, le 1er élément est la valeur que l’on veut répéter et la 2ᵉ est le nombre de fois que l’on veut la répéter.

Pour accéder aux éléments d’un array on va utiliser la syntaxe avec les crochets :

fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
}

Si on essaye d’accéder à un index qui n’existe pas, rust va panic, c’est-à-dire crasher en générant une erreur.