Aller au contenu

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 :

Rust
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 :

Text Only
$ 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 :

Rust
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 :

Rust
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 :

Rust
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 :

Rust
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 :

Rust
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

Rust
let a: [i32; 5] = [1, 2, 3, 4, 5];

On peut accéder aux éléments d'un tableau comme ceci :

Rust
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.