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
oufalse
; - 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.
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 = 5
i32; // 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
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 : 0
x, 0
o ou 0
b.
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 ».
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 =
{}
"
, 1
u32 + 2
);
// Soustraction d'entiers
println!
("1 - 2 =
{}
"
, 1
i32 - 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}
"
, 0b0011
u32 &
0b0101
);
println!
("0011 OR 0101 is
{:04b}
"
, 0b0011
u32 | 0b0101
);
println!
("0011 XOR 0101 is
{:04b}
"
, 0b0011
u32 ^ 0b0101
);
println!
("1 << 5 is
{}
"
, 1
u32 << 5
);
println!
("0x80 >> 2 is 0x
{:x}
"
, 0x80
u32 >> 2
);
// Utilisez des underscores pour améliorer la lisibilité !
println!
("One million is written as
{}
"
, 1_000_000
u32);
}
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é.
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 = (1
u8, 2
u16, 3
u32, 4
u64,
-1
i8, -2
i16, -3
i32, -4
i64,
0
.1
f32, 0
.2
f64,
'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 = ((1
u8, 2
u16, 2
u32), (4
u64, -1
i8), -2
i16);
// 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:
{:?}
"
, (5
u32,));
println!
("just an integer:
{:?}
"
, (5
u32));
// 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é
- 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 :
( 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.
- 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 :
println!(
"Matrix:\n{}"
, matrix);
println!(
"Transpose:\n{}"
, transpose
(
matrix));
Affiche :
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].
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]);
}