Nous utiliserons le langage de programmation Rust pour les TPs SdE2.
Par défaut, Rust a un ensemble d'éléments définis dans la bibliothèque standard qu'il introduit dans le cadre d'application de chaque programme. Cet ensemble s'appelle le prélude, et vous pouvez voir tout ce qu'il contient dans la documentation standard de la bibliothèque.
Si un type que vous voulez utiliser n'est pas dans le prélude, vous devez mettre ce type dans la portée explicitement avec une instruction use. L'utilisation de la bibliothèque std :: io vous offre un certain nombre de fonctionnalités utiles, notamment la possibilité d'accepter les entrées de l'utilisateur.
use std::io;
La fonction main est le entry point de notre programme.
fn main() { println!("Hello, world!"); }
On utilise println!() pour imprimer des messages sur l'ecran.
Pour insérer un placeholder dans le println! méthode, utilisez une paire d'accolades {}. Nous fournissons le nom ou l'expression de la variable pour remplacer le placeholder fourni en dehors de la chaîne.
fn main() { let name = "Mary"; let age = 26; println!("Hello, {}. You are {} years old", name, age); }
On utilise le mot-clé let pour créer un variable.
let a = 5;
Par défaut, en Rust, les variables sont immuables, ça veut dire, une fois qu'une valeur est liée à un nom, vous ne pouvez pas modifier cette valeur.
Exemple:
fn main() { let x = 5; println!("The value of x is: {x}"); x = 6; println!("The value of x is: {x}"); }
Dans ce cas, on va obtenir un erreur de compilation parce qu’on essaye de modifier la valeur de x de 5 à 6, mais x est immuable, donc on ne peut pas faire cette modification.
Bien que les variables soient immuables par défaut, vous pouvez les rendre modifiables en ajoutant mut devant le nom de la variable. L'ajout de mut transmet également l'intention aux futurs lecteurs du code en indiquant que d'autres parties du code modifieront la valeur de cette variable.
fn main() { let mut x = 5; println!("The value of x is: {x}"); x = 6; println!("The value of x is: {x}"); }
Maintenant, la valeur de x peut devenir 6.
Comme les variables immuables, les constantes sont des valeurs qui sont liées à un nom et ne sont pas autorisées à changer, mais il existe quelques différences entre les constantes et les variables.
Tout d'abord, vous n'êtes pas autorisé à utiliser mut avec des constantes. Les constantes ne sont pas seulement immuables par défaut, elles sont toujours immuables. Vous déclarez des constantes en utilisant le mot clé const au lieu du mot clé let.
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
Un type scalaire représente une valeur unique. Rust a quatre types scalaires principaux : entiers, nombres à virgule flottante, booléens et caractères.
Integer → Chaque variante peut être signée ou non signée et a une taille explicite.
let x: i8 = -2; let y: u16 = 25;
Virgule flottante → Les types à virgule flottante de Rust sont f32 et f64, qui ont respectivement une taille de 32 bits et 64 bits. Le type par défaut est f64 car sur les processeurs modernes, c'est à peu près la même vitesse que f32 mais il est capable de plus de précision. Tous les types à virgule flottante sont signés.
fn main() { let x = 2.0; // f64 let y: f32 = 3.0; // f32 }
Boolean → Les booléens ont une taille d'un octet. Le type booléen dans Rust est spécifié à l'aide de bool.
let t = true; let f: bool = false; // with explicit type annotation
Caractere → Le type char de Rust est le type alphabétique le plus primitif du langage.
let c = 'z'; let z: char = 'ℤ'; // with explicit type annotation let heart_eyed_cat = '😻';
Tuple → Un tuple est une manière générale de regrouper un certain nombre de valeurs avec une variété de types en un seul type composé. Les tuples ont une longueur fixe : une fois déclarés, leur taille ne peut pas augmenter ou diminuer.
let tup: (i32, f64, u8) = (500, 6.4, 1);
Array → Contrairement à un tuple, chaque élément d'un tableau doit avoir le même type. Contrairement aux tableaux de certains autres langages, les tableaux de Rust ont une longueur fixe.
let a = [1, 2, 3, 4, 5];
Nous définissons une fonction dans Rust en entrant fn suivi d'un nom de fonction et d'un ensemble de parenthèses. Les accolades indiquent au compilateur où commence et se termine le corps de la fonction.
fn main() { println!("Hello, world!"); another_function(); } fn another_function() { println!("Another function."); }
Nous pouvons définir des fonctions avec des paramètres, qui sont des variables spéciales qui font partie de la signature d'une fonction. Lorsqu'une fonction a des paramètres, vous pouvez lui fournir des valeurs concrètes pour ces paramètres.
fn main() { another_function(5); } fn another_function(x: i32) { println!("The value of x is: {x}"); }
Les corps de fonction sont constitués d'une série d'instructions se terminant éventuellement par une expression.
Les déclarations sont des instructions qui effectuent une action et ne renvoient pas de valeur.
Les expressions évaluent une valeur résultante.
Les fonctions peuvent renvoyer des valeurs au code qui les appelle. Nous ne nommons pas les valeurs de retour, mais nous devons déclarer leur type après une flèche (→). En Rust, la valeur de retour de la fonction est synonyme de la valeur de l'expression finale dans le bloc du corps d'une fonction. Vous pouvez revenir plus tôt à partir d'une fonction en utilisant le mot-clé return et en spécifiant une valeur, mais la plupart des fonctions renvoient implicitement la dernière expression.
fn five() -> i32 { 5 } fn main() { let x = five(); println!("The value of x is: {x}");// "The value of x is: 5" }
Toutes les expressions if commencent par le mot-clé if, suivi d'une condition. Facultativement, nous pouvons également inclure une expression else.
fn main() { let number = 3; if number < 5 { println!("condition was true"); } else { println!("condition was false"); } }
Vous pouvez utiliser plusieurs conditions en combinant if et else dans une expression else if:
fn main() { let number = 6; if number % 4 == 0 { println!("number is divisible by 4"); } else if number % 3 == 0 { println!("number is divisible by 3"); } else if number % 2 == 0 { println!("number is divisible by 2"); } else { println!("number is not divisible by 4, 3, or 2"); } }
Parce que si est une expression, nous pouvons l'utiliser sur le côté droit d'une instruction let pour affecter le résultat à une variable.
fn main() { let condition = true; let number = if condition { 5 } else { 6 }; println!("The value of number is: {number}");//"The value of the number is 5" }
Le mot-clé loop indique à Rust d'exécuter un bloc de code encore et encore pour toujours ou jusqu'à ce que vous lui disiez explicitement de s'arrêter.
fn main() { loop { println!("again!"); } }
L'une des utilisations d'une boucle est de réessayer une opération dont vous savez qu'elle pourrait échouer, comme vérifier si un thread a terminé son travail. Vous devrez peut-être également transmettre le résultat de cette opération hors de la boucle au reste de votre code. Pour ce faire, vous pouvez ajouter la valeur que vous souhaitez renvoyer après l'expression break que vous utilisez pour arrêter la boucle ; cette valeur sera renvoyée hors de la boucle afin que vous puissiez l'utiliser:
fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; println!("The result is {result}"); }
fn main() { let mut number = 3; while number != 0 { println!("{number}!"); number -= 1; } println!("LIFTOFF!!!"); }
fn main() { let a = [10, 20, 30, 40, 50]; for element in a { println!("the value is: {element}"); } }
Les structures sont similaires aux tuples, en ce sens qu'elles contiennent les deux plusieurs valeurs liées. Comme les tuples, les morceaux d'une structure peuvent être de différents types. Contrairement aux tuples, dans une structure, vous nommerez chaque élément de données afin que la signification des valeurs soit claire.
Pour définir une structure, nous entrons le mot-clé struct et nommons la structure entière. Le nom d'une structure doit décrire la signification des éléments de données regroupés. Ensuite, entre accolades, nous définissons les noms et les types des données, que nous appelons champs.
struct User { active: bool, username: String, email: String, sign_in_count: u64, }
Pour utiliser une structure après l'avoir définie, nous créons une instance de cette structure en spécifiant des valeurs concrètes pour chacun des champs. Nous créons une instance en indiquant le nom de la structure, puis ajoutons des accolades contenant des paires clé : valeur, où les clés sont les noms des champs et les valeurs sont les données que nous voulons stocker dans ces champs.
fn main() { let user1 = User { active: true, username: String::from("someusername123"), email: String::from("someone@example.com"), sign_in_count: 1, }; }
Pour acceder a un certain membre du struct on utilise cette syntaxe:
fn main() { let mut user1 = User { active: true, username: String::from("someusername123"), email: String::from("someone@example.com"), sign_in_count: 1, }; user1.email = String::from("anotheremail@example.com");
Comme pour toute expression, nous pouvons construire une nouvelle instance de la structure en tant que dernière expression dans le corps de la fonction pour renvoyer implicitement cette nouvelle instance.
fn build_user(email: String, username: String) -> User { User { active: true, username: username, email: email, sign_in_count: 1, } }
Rust prend également en charge les structures qui ressemblent aux tuples, appelées tuple structs. Les structures de tuple ont la signification supplémentaire fournie par le nom de la structure mais n'ont pas de noms associés à leurs champs ; au lieu de cela, ils ont juste les types des champs. Les structures de tuple sont utiles lorsque vous souhaitez donner un nom à l'ensemble du tuple et lui donner un type différent des autres tuples, et lorsque nommer chaque champ comme dans une structure régulière serait verbeux ou redondant.
struct Color(i32, i32, i32); struct Point(i32, i32, i32); fn main() { let black = Color(0, 0, 0); let origin = Point(0, 0, 0); }
Il existe 2 types de string en Rust: String et &str. Pour aujourd’hui on va utiliser String (qui fait partie de la bibliothèque std::string). Pour créer un String on utilise la méthode:
let s: String = String::from(“some string here”);
On va apprendre dans les prochains laboratoires quelles sont les différences entre String et &str et quand on doit utiliser chacun d’entre eux.
On doit acceder a la directeur ou le fichier main.rs se trouve et executee la comande:
cargo run
Hint: utilisez io::stdin().read_line(&mut buffer) pour lire a partir du clavier.