Rust - Types de données
Chaque valeur en Rust a un type, ce qui lui indique quel type de donnée est spécifiée. Il y a deux catégories de types : Les types scalaires et les types composés
Rust est un language avec un typage statique, ce qui veut dire qu'il doit savoir le type de toute les variables. Le compileur peut dans la plupart des cas deviner le type d'une variable selon le type de donnée et ce que l'on en fait. Dans les cas ou il y a plusieurs types possibles pour une valeur, il est recommendé de spécifier le type de variable que l'on veut obtenir, par exemple quand on veut convertir une chaine de caractères en un nombre, comme dans l'exemple suivant :
let guess: u32 = "42".parse().expect("Not a number!");
Si on ajoute pas le : u32
, on aura une erreur de compilation qui indique que le compileur a besoin de plus d'indications concerant le type de la variable souhaitée :
$ cargo build
Compiling no_type_annotations v0.1.0 (file:///projects/no_type_annotations)
error[E0282]: type annotations needed
--> src/main.rs:2:9
|
2 | let guess = "42".parse().expect("Not a number!");
| ^^^^^
|
help: consider giving `guess` an explicit type
|
2 | let guess: _ = "42".parse().expect("Not a number!");
| +++
For more information about this error, try `rustc --explain E0282`.
error: could not compile `no_type_annotations` due to previous error
Types scalaires
Nombres entiers
Un entier est un nombre sans chiffres après la virgule. Dans l'exemple d'au dessus, on à utilisé le type u32
. Ce type indique que le nombre est un entier naturel qui prend 32 bits d'espace mémoire. Le tableau ci dessous montre les types d'entiers intégrés a Rust :
Longueur | Entiers naturels | Entiers relatifs |
---|---|---|
8 Bits | u8 |
i8 |
16 Bits | u16 |
i16 |
32 Bits | u32 |
i32 |
128 Bits | u128 |
i128 |
Architecture | usize |
isize |
Le type usize
et isize
dépendent de l'architecture du processeur de la machine exécutant le programme : le nombre sera stocké sur 64 Bits si c'est un processeur 64 bits ou 32 si c'est un processeur 32 bits.
On peut écrire des entiers sous n'importe quelle forme présente dans le tableau ci-dessous :
Littéral de nombre | Exemple |
---|---|
Décimal | 98_222 |
Hexadécimal | 0xff |
Octal | 0o77 |
Binaire | 0b1111_0000 |
Octet | b'A' (Type u8 seulement) |
Si on ne sait pas quel type choisir, les valeurs par défaut de Rust sont un bon point de départ : le type d'entier par défaut est u32
.
Débordement d'entiers
Admetons un nombre de type u8
qui peut stocker des valeurs entre 0 et 255. Si on essaye de mettre un nombre qui n'est pas dans cet intervale, un débordement d'entier va se produire, qui peut conduire a 2 situations. Lorsqu'on compile en mode déboguage, Rust inclut des vérifications qui va faire paniquer le programme si cela se produit. Rust panique lorsqu'il rencontre une erreur a l'exécution qui n'est pas récupérable.
Lorsqu'on compile en mode release
, Rust n'inclut pas ces vérifications. Si cela se produit, Rust va faire un emballage a deux compléments. Pour la faire court, les valeurs plus grandes que le maximum qu'une valeur peut contenir peuvent "rembobiner" jusqu'au minimum que la variable peut contenir.
On considère généralement le fait de d'appuyer sur ce genre de comportement pour ses programmes comme une erreur de programmation.
Nombres flotants
Rust a deux types de nombres flotants (a virgule) : f32
et f64
. Ils sont tout les deux signés. Le type par défaut est f64
car il est aussi rapide a utiliser que f32
sur les processeurs modernes.
Opérations numériques
Rust supporte toutes les opérations mathématiques courantes entre les nombres. Les résultats des divisions d'entiers sont arrondis à l'entier le plus proche. Voici un exemple :
fn main() {
// addition
let sum = 5 + 10;
// Soustraction
let difference = 95.5 - 4.3;
// multiplication
let produit = 4 * 30;
// division
let quotient = 56.7 / 32.2;
let tronqué = -5 / 3; // = -1
// reste
let reste = 43 % 5;
}
Booléens
Comme la plupart des languages de programmation, un type booléen en Rust a deux valeurs : true
et false
. Les booléens prennent seulement un bit d'espace mémoire. Le type est spécifié par bool
.
Caractère
Le type char
en Rust est le type alphabétique le plus simple. Voici des exemples de déclaration :
fn main() {
let c = 'z';
let z: char = 'ℤ'; // Avec annotation de type explicite
let heart_eyed_cat = '😻';
}
On spécifie des litérals de caractère par un guillemet simple contrairement au littérals de chaine de caractères qui sont spécifiés avec des guillemets doubles. Les caractères sont stockés sous leur code Unicode. Il supporte les caractères de U+0000
à U+D7FF
et U+E000
à U+10FFFF
.
Types composés
Les types composés peuvent rassembler plusieurs types de données en un type. Il y a deux principaux types : les Tuples et les arrays
Tuples
Les tuples sont une manière générale de grouper des éléments enssemble. Les tuples ont une taille fixe : une fois déclarée, un tuple ne peut ni grandir, ni rétrécir.
On crée un tuple avec une liste de valeurs séparées par des virgules dans des parenthèses. Chaque position dans un tuple a un type, et le type des valeurs contenues dans le tuple n'a pas besoin d'être les mêmes. Dans l'exemple suivant, on à ajouté une annotation de type :
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
On peut accéder aux éléments d'un tuple avec un point suivi de l'index de la valeur qu'on veut récupérer. Par exemple :
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
Tableaux
Une autre facon de stocker des valeurs est l'utilisation d'un tableau. Contrairement aux tuples, tous les éléments doivent avoir le même type. Egalement en contradiction avec d'autres languages de programmation, les tableaux ont une taille fixe. On crée un tableau comme ceci :
let months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"];
On peut définit le type d'un tableau avec des crochets avec le type de chaque élément et la taille, séparés par une virgule, comme dans l'exemple
let a: [i32; 5] = [1, 2, 3, 4, 5];
On peut accéder aux éléments d'un tableau comme ceci :
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}
On ne peut pas accéder a un élément d'indice suppérieur a la tailel d'un tableau : cela causera une erreur d'exécution et une panique.