IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Rust par l'exemple


précédentsommairesuivant

2. Les primitifs

Le langage Rust offre une grande variété de primitifs. Liste non-exhaustive :

  • Les entiers signés : i8, i16, i32, i64 et isize (dépend de l'architecture de la machine) ;
  • Les entiers non-signés : u8, u16, u32, u64, usize (dépend de l'architecture de la machine) ;
  • Les réels : f32, f64 ;
  • Les caractères (Unicode) : 'a', 'α', '∞'. Codés sur 4 octets ;
  • Les booléens : true ou false ;
  • L'absence de type (), qui n'engendre qu'une seule valeur : () ;
  • Les tableaux : [1, 2, 3] ;
  • Les tuples : (1, true).

Le type des variables peut toujours être spécifié. Les nombres peuvent également être typés grâce à un suffixe, ou par défaut (laissant le compilateur les typer). Les entiers, par défaut, sont typés i32 tandis les réels sont typés f64.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
fn main() {
    // Le type des variables peut être spécifié, annoté.
    let logical: bool = true;

    let a_float: f64 = 1.0;  // typage classique
    let an_integer   = 5i32; // Typage par suffixe

    // Le type par défaut peut également être conservé.
    // typage implicite
    let default_float   = 3.0; // `f64`
    let default_integer = 7;   // `i32`

    let mut mutable = 12; // Entier signé codé sur 4 octets (i32).

    // Erreur! Le type d'une variable ne peut être modifié en cours de route.
    // mutable = true;
}

Voir aussi

La bibliothèque standard.

2-1. Les littéraux et les opérateurs

Les entiers (1), les réels (1,2), les caractères ('a'), les chaînes de caractères ("abc"), les booléens (true) et l'absence de type () (un tuple vide) peuvent être représentés en utilisant les littéraux.

Les entiers peuvent également être exprimés sous différentes bases : hexadécimal, octal ou binaire en utilisant, respectivement, les préfixes : 0x, 0o ou 0b.

Des underscores peuvent être insérés à l'intérieur des littéraux numériques pour soigner la lisibilité (e.g. 1_000 est équivalent à 1000 et 0.000_001 est équivalent à 0.000001).

Nous devons renseigner le compilateur quant au type de littéral que nous utilisons. Pour le moment, nous allons utiliser le suffixe u32 pour indiquer que le littéral est un entier non-signé codé sur 32 bits et le suffixe i32 pour indiquer que c'est un entier signé codé sur 32 bits.

Les opérateurs et leur priorité dans le langage Rust peuvent être retrouvés dans les langages « C-like ».

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
fn main() {
    // Addition d'entiers
    println!("1 + 2 = {}", 1u32 + 2);

    // Soustraction d'entiers
    println!("1 - 2 = {}", 1i32 - 2);
    // TODO ^ Essayez de changer `1i32` par `1u32` pour prendre conscience de l'importance du type

    // Logique booléenne
    println!("true AND false is {}", true && false);
    println!("true OR false is {}", true || false);
    println!("NOT true is {}", !true);

    // Opérations bit-à-bit
    println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101);
    println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101);
    println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101);
    println!("1 << 5 is {}", 1u32 << 5);
    println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2);

    // Utilisez des underscores pour améliorer la lisibilité !
    println!("One million is written as {}", 1_000_000u32);
}

2-2. Les tuples

Un tuple est une collection de valeurs de différents types (ou pas). Les tuples peuvent être construits en utilisant les parenthèses () et chaque tuple est lui-même un type possédant sa propre signature (T1, T2, …), où T1, T2 sont les types de ses membres. Les fonctions peuvent se servir des tuples pour renvoyer plusieurs valeurs, puisque ces derniers peuvent être extensibles à volonté.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
// Les tuples peuvent être utilisés comme arguments passés à une fonction 
// et comme valeurs de renvoi.
fn reverse(pair: (i32, bool)) -> (bool, i32) {
    // `let` peut être utilisé pour assigner, lier les membres d'un tuple à des 
    // variables.
    let (integer, boolean) = pair;

    (boolean, integer)
}

// La structure suivante est dédiée à l'activité.
#[derive(Debug)]
struct Matrix(f32, f32, f32, f32);

fn main() {
    // Un tuple composé de différents types
    let long_tuple = (1u8, 2u16, 3u32, 4u64,
                      -1i8, -2i16, -3i32, -4i64,
                      0.1f32, 0.2f64,
                      'a', true);

    // Les valeurs peuvent être extraites depuis le tuple en utilisant son 
    // indexation.
    println!("long tuple first value: {}", long_tuple.0);
    println!("long tuple second value: {}", long_tuple.1);

    // Les tuples peuvent être eux-même des membres d'un tuple.
    let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);

    // Les tuples peuvent être affichés avec Debug.
    println!("tuple of tuples: {:?}", tuple_of_tuples);

    let pair = (1, true);
    println!("pair is {:?}", pair);

    println!("the reversed pair is {:?}", reverse(pair));

    // Pour créer un élément de tuple, une virgule est requise pour différencier 
    // un élément de tuple d'un simple litéral entouré de parenthèses.
    println!("one element tuple: {:?}", (5u32,));
    println!("just an integer: {:?}", (5u32));

    // Les tuples peuvent être "déstructurés" (i.e. décomposés pour créer de 
    // nouvelles assignations).
    let tuple = (1, "hello", 4.5, true);

    let (a, b, c, d) = tuple;
    println!("{:?}, {:?}, {:?}, {:?}", a, b, c, d);

    let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
    println!("{:?}", matrix)

}

Activité

  1. Récapitulatif : Implémentez les services du trait fmt::Display pour la structure Matrix dans l'exemple ci-dessus. Donc si vous passez de l'affichage de débogage {:?} à l'affichage plus « user friendly » {}, vous devriez voir le résultat suivant :
 
Sélectionnez
( 1.1 1.2 )
( 2.1 2.2 )

Vous pouvez vous référer à l'exemple précédemment donné pour l'implémentation du trait Display.

  1. Ajoutez une fonction transpose(), en vous appuyant sur l'implémentation de la fonction reverse(), qui accepte une matrice en paramètre et renvoie une matrice dans laquelle deux éléments ont été inversés. Exemple :
 
Sélectionnez
println!("Matrix:\n{}", matrix);
println!("Transpose:\n{}", transpose(matrix));

Affiche :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
Matrix:
( 1.1 1.2 )
( 2.1 2.2 )
Transpose:
( 1.1 2.1 )
( 1.2 2.2 )

2-3. Les tableaux et les "slices"

Un tableau est une collection d'objets appartenant au même type T, contenu dans un bloc mémoire défragmenté. Vous pouvez créer un tableau en utilisant les crochets [] et leur taille, connue à la compilation, fait partie intégrante de la signature du type [T; taille].

Note : Le terme « slice », en français, pourrait être traduit par « morceau », « tranche », g. Pour la suite du chapitre, nous utiliserons le terme « slice ».

Les slices sont similaires aux tableaux, à l'exception de leur taille qui n'est pas connue à la compilation. Une slice est un objet composé de deux « mots », le premier étant un pointeur vers la ressource initiale et le second étant la taille de la slice. La taille en mémoire de la slice est déterminée par l'architecture du processeur (e.g. 64 bits pour une architecture x86-64). Les slices peuvent être utilisées pour isoler une partie d'un tableau et héritent de la signature de ce dernier &[T].

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
use std::mem;


// Cette fonction emprunte une slice.
fn analyze_slice(slice: &[i32]) {
    println!("first element of the slice: {}", slice[0]);
    println!("the slice has {} elements", slice.len());
}

fn main() {
    // Tableau dont la taille est connue à la compilation (le type peut être omis).
    let xs: [i32; 5] = [1, 2, 3, 4, 5];

    // Tous les éléments peuvent être initialisés à la même valeur.
    let ys: [i32; 500] = [0; 500];

    // L'indexation débute à 0.
    println!("first element of the array: {}", xs[0]);
    println!("second element of the array: {}", xs[1]);

    // `len` renvoie la taille du tableau.
    println!("array size: {}", xs.len());

    // Les tableaux sont alloués dans la pile.
    println!("array occupies {} bytes", mem::size_of_val(&xs));

    // Les tableaux peuvent être automatiquement empruntés en tant que 
    // slice.
    println!("borrow the whole array as a slice");
    analyze_slice(&xs);

    // Les slices peuvent pointer sur une partie bien précise d'un tableau.
    println!("borrow a section of the array as a slice");
    analyze_slice(&ys[1 .. 4]);

    // Erreur! Le dépassement de tampon fait planter le programme.
    // println!("{}", xs[5]);
}

précédentsommairesuivant

Licence Creative Commons
Le contenu de cet article est rédigé par Rust Core Team et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.