Rust (langage)
Rust est un langage de programmation compilĂ© multi-paradigme conçu et dĂ©veloppĂ© par Mozilla Research depuis 2010[6] - [7]. Il a Ă©tĂ© conçu pour ĂȘtre « un langage fiable, concurrent, pratique »[8] - [9], supportant les styles de programmation purement fonctionnel, modĂšle d'acteur, procĂ©dural, ainsi qu'orientĂ© objet sous certains aspects[10].
Rust | ||
Date de premiĂšre version | ||
---|---|---|
Paradigmes | Impératif, fonctionnel, concurrent | |
Auteur | Graydon Hoare | |
DĂ©veloppeurs | Mozilla | |
DerniĂšre version | 1.70.0 ()[1] | |
Typage | Fort, statique | |
Influencé par | C++, Erlang, Haskell, Scala, OCaml, Scheme, Swift, C#, Alef, Limbo[2] | |
A influencé | Swift[3] | |
SystĂšme d'exploitation | Multiplate-forme | |
Licence | Licence Apache version 2.0[4] - [5] et licence MIT[4] - [5] | |
Site web | www.rust-lang.org | |
Extension de fichier | rs | |
En 2020, ses domaines de prédilection sont la programmation systÚme, les applications en ligne de commande, les applications Web via WebAssembly, les services réseaux et les systÚmes embarqués.
Du fait de la politique de Mozilla[11], Rust est entiÚrement développé de façon ouverte (les ingénieurs de Mozilla Research publient leurs idées et les décisions prises lors des réunions) et sollicite les remarques et contributions de la communauté. La conception du langage est graduellement améliorée au travers des retours de l'équipe travaillant sur le moteur de rendu Servo[12] et de façon pragmatique lors de l'écriture du compilateur. Bien que le projet soit financé par Mozilla, la majorité des contributions proviennent de la communauté.
Description
Rust se veut un langage performant, sûr et productif[13].
Le langage peut notamment donner des garanties d'absence d'erreur de segmentation ou de situation de concurrence[14] dÚs l'étape de compilation. De plus, ceci se fait sans ramasse-miettes[15]. Ses performances sont comparables à celles de C ou C++[16] pour ce qui concerne la vitesse d'exécution.
Enfin, Rust est accompagné de Cargo, un gestionnaire de paquets permettant de gérer la compilation et les dépendances entre paquets. Le compilateur fournit des messages d'erreur explicites et utiles. Il existe aussi d'autres outils d'édition pour les EDI ainsi qu'une documentation abondante.
Histoire
Le langage s'est dĂ©veloppĂ© Ă partir d'un projet personnel de Graydon Hoare, initiĂ© en 2006. Son employeur, Mozilla, commença sa participation en 2009[17] et rĂ©vĂ©la officiellement ses travaux en 2010[18]. La mĂȘme annĂ©e, le projet passa du compilateur initialement utilisĂ© (Ă©crit en OCaml) au compilateur auto-hĂ©bergĂ© Ă©crit en Rust[19]. Ce compilateur, connu sous le nom de rustc, s'est compilĂ© avec succĂšs en 2011[20]. Le compilateur auto-hĂ©bergĂ© utilise LLVM en arriĂšre-fond.
La premiÚre version alpha numérotée du compilateur Rust apparait en janvier 2012[21].
La premiĂšre version stable de Rust, 1.0, est sortie en 2015.
En 2017, Mozilla annonce que Rust supporte la compilation vers le jeu d'instruction de l'architecture RISC-V[22].
Caractéristiques
Syntaxe
La syntaxe du langage est similaire à celle du C, étant constituée de blocs de code délimités par des accolades et de structures de contrÎle comme if
, else
, while
et for
.
Cependant, la sémantique de Rust est assez différente. En effet, les blocs et les structures de contrÎles sont des expressions, comme on peut le voir dans l'exemple :
let x = if n < 10 { n } else { n - 10 };
En C, une telle opération n'est pas «intelligible» pour le compilateur; il faudra soit encapsuler le bloc conditionnel dans une autre fonction, soit utiliser un opérateur ternaire int x = (n < 10) ? n : n - 10;
.
L'utilisation d'expressions rapproche ainsi Rust de langages fonctionnels comme Haskell ou OCaml.
Valeur et mutabilité
Dans la plupart des langages, une variable est modifiable par défaut. Rust inverse cette logique en privilégiant la constance : le mot-clé let
dĂ©clare par dĂ©faut des variables immuables (immutable variable en anglais) qui ne peuvent ĂȘtre affectĂ©es qu'une seule fois, mais dont la valeur peut ĂȘtre dĂ©finie au moment de l'exĂ©cution. Il est nĂ©cessaire de rajouter le mot-clĂ© mut
pour rendre une variable « mutable » ou « muable » : ainsi, on restreint les variables qui sont effectivement autorisées à changer. Le type des variables est inféré à chaque fois que c'est possible.
Pour les valeurs constantes connues à la compilation, le mot-clé const
remplace let
. Leur type doit ĂȘtre prĂ©cisĂ© et elles doivent ĂȘtre initialisĂ©es Ă partir d'une expression constante, excluant les rĂ©sultats d'appels de fonctions[23].
fn main() {
// DĂ©claration de variables
let mut a = 5; // a est une variable modifiable
let b = a * 2; // b est non modifiable et du mĂȘme type que a
// Constantes
const c: u32 = 5; // déclaration d'une constante entiÚre non-signée
const c: u8 = b - 3; // interdit car `b - 3` n'est pas une expression constante (b non défini à la compilation)
const c = 5; // interdit car le type de c n'est pas précisé
// Altération
c = 3; // illégal car c est une constante
b = 3; // illégal car b est une variable immuable
a = 2; // autorisé car a est déclaré comme "mut"
let a = a + 5; // autorisé une nouvelle variable a est créée valant 7,
// l'ancienne variable a est "couverte" par la nouvelle (shadowing)
// VĂ©rification des valeurs
assert_eq!(a, 5); // faux
assert_eq!(b, 10); // vrai
}
ĂnumĂ©rations et filtrage par motif
Rust permet la définition de types sommes (ou énumérations) à l'aide du mot-clé enum
. On peut utiliser ces types sommes avec du filtrage par motif, en utilisant par exemple le mot-clé match
.
Exemple :
// On crée un type « Forme » pour décrire des formes géométriques.
enum Forme {
Point, // une forme peut ĂȘtre un point, sans donnĂ©es attachĂ©es.
Rectangle(f64, f64), // une forme peut ĂȘtre un rectangle, caractĂ©risĂ© par les longueurs de ses cĂŽtĂ©s.
Cercle(f64), // une forme peut ĂȘtre un cercle, caractĂ©risĂ© par son rayon.
}
// Calcule l'aire d'une forme géométrique.
fn aire(f: Forme) -> f64 {
match f {
// Filtrage par motif avec « match »
Forme::Point => 0.0,
Forme::Cercle(rayon) => 3.14 * rayon * rayon,
Forme::Rectangle(cote_a, cote_b) => cote_a * cote_b,
}
}
Certaines énumérations font partie de la bibliothÚque standard, comme Option, permettant d'éviter l'utilisation du pointeur NULL[24].
Programmation générique
Afin de garantir la programmation gĂ©nĂ©rique, le langage Rust implĂ©mente son propre systĂšme de mĂ©taprogrammation basĂ© sur les traits. Ainsi, un dĂ©veloppeur souhaitant mettre en place une fonction gĂ©nĂ©rique sera dans lâobligation dâexpliciter les diffĂ©rents traits utilisĂ©s dans sa fonction, son objet ou sa mĂ©thode.
Exemple de lâimplĂ©mentation du tri-bulle de maniĂšre gĂ©nĂ©rique:
fn tri_bulle<T: std::cmp::PartialOrd>(liste: &mut Vec<T>) {
// Ici, on a besoin de lâopĂ©rateur de comparaison < implĂ©mentĂ© par le trait
// PartialOrd
let mut i = 0;
while i < (liste.len() - 1) {
if liste[i] < liste[i + 1] {
liste.swap(i, i + 1);
i = 0;
} else {
i += 1;
}
}
}
Ainsi, toute liste de valeurs supportant lâopĂ©rateur de comparaison < pourra ĂȘtre passĂ©e comme argument pour cette fonction.
Définition des méthodes communes par le biais de trait
Les traits sont assimilables aux interfaces en java ou aux classes abstraites en C++: Ils dĂ©finissent les mĂ©thodes qui seront proposĂ©es par les structures les implĂ©mentant. Ils sont des composants centraux du langage, Ă©tant donnĂ© que les opĂ©rations comme les additions sont dĂ©finies via lâimplĂ©mentation de traits. Ces derniers peuvent Ă©galement proposer une implĂ©mentation gĂ©nĂ©rique de certaines de leur fonctions Ă condition que ces derniĂšres ne requiĂšrent pas lâutilisation de donnĂ©es stockĂ©es dans les objets.
Exemple de dĂ©finition dâun trait:
trait Polygone {
fn nombre_cotes(&self) -> usize;
fn points(&self) -> Vec<(f32, f32)>;
// Il nâexiste pas de mĂ©thode simple et gĂ©nĂ©rique pour calculer lâaire dâun
// polygone
fn aire(&self) -> f32;
fn perimetre(&self) -> f32 {
let mut ret = 0.0;
let points = self.points();
for i in 1..points.len() {
ret += f32::sqrt(
f32::pow(points[i].0 - points[i - 1].0, 2)
+ f32::pow(points[i].1 - points[i - 1].1, 2),
);
}
return ret;
}
}
Possession et emprunt
Pour obtenir des garanties de sûreté, Rust utilise les concepts d'ownership (propriété ou possession) et de borrowing (emprunt).
Ainsi, une valeur a toujours un seul propriétaire. Si la valeur change de propriétaire, l'ancien propriétaire ne peut plus l'utiliser.
Par exemple :
fn prend_possession(v: Vec<i32>) {
// Cette fonction prend possession de son paramĂštre v et ne la rend pas.
println!("{:?}", v);
}
fn main() {
let mut a = vec![1, 2, 3]; // a est le propriétaire du vecteur.
let mut b = a; // b est maintenant le propriétaire du vecteur.
// pas clair,
a.push(4); // erreur de compilation : a n'a plus le droit d'utiliser ce vecteur
prend_possession(b);
b.push(5); // erreur de compilation : b n'a plus le droit d'utiliser ce vecteur
}
Pour utiliser une valeur Ă plusieurs endroits Ă la fois, il est possible de prĂȘter cette valeur en crĂ©ant des rĂ©fĂ©rences.
Il est possible de créer :
- Soit des références immuables, avec l'opérateur
&
. - Soit une référence muable, avec l'opérateur
& mut
.
En particulier, il n'est pas possible de mélanger les références muables et immuables.
Exemple :
fn take_reference(v: &Vec<i32>) {
// Cette fonction prend une référence vers un vecteur
println!("{:?}", v);
}
fn correct() {
let a = vec![1, 2, 3];
let ref_1 = &a;
let ref_2 = &a;
// On crée plusieurs références immuables vers a que l'on peut passer à des fonctions.
// Faire ceci ne serait pas possible si l'on travaillait avec une fonction qui prend
// l'ownership de a.
take_reference(ref_1);
take_reference(ref_2);
}
fn incorrect() {
let mut a = vec![1, 2, 3];
// Ce code ne compile pas.
// En effet, on travaille à la fois avec une référence muable vers a (ref_1),
// et à la fois avec une référence immuable vers a (ref_2).
let ref_1 = &mut a[0];
let ref_2 = &a;
println!("{}", *ref_1);
take_reference(ref_2);
}
Points forts
Rust repose sur des concepts connus et Ă©prouvĂ©s (d'oĂč le nom Rust, « la rouille » en anglais) et n'intĂšgre pas de concepts nouveaux et non testĂ©s[25][26]. Ces concepts ont Ă©tĂ© empruntĂ©s Ă des langages de programmation existants et assemblĂ©s dans un seul langage[27] :
- orientation bas niveau, avec la possibilité de choisir la gestion de la mémoire adaptée au programme : pile, tas (notamment via l'utilisation de pointeurs intelligents) ;
- gestion de la concurrence intégrée dans le langage ;
- sûr par défaut, avec la possibilité de contourner cette sûreté dans les blocs unsafe :
- typage statique sans conversions implicites,
- accÚs mémoire validés statiquement par le compilateur,
- variables immuables par défaut,
- passage de messages entre tùches concurrentes pour éviter des erreurs liées aux accÚs concurrents à la mémoire,
- pas d'utilisation de null ;
- inférence de types ;
- filtrage par motif ;
- généricité.
Rust est souvent dĂ©crit comme l'un des successeurs potentiels de C et C++[28] (avec D et, dans une moindre mesure, Go) notamment grĂące Ă sa sĂ»retĂ© et sa rapiditĂ© â c'est un objectif clairement affichĂ© par les dĂ©veloppeurs.
Projets basés sur Rust
- Firefox[29] :
- Cargo : gestionnaire de paquets Rust ;
- Redox : un systĂšme d'exploitation de type Unix ;
- Deno : successeur Ă Node.js ;
- exa (en) : alternative Ă ls ;
- fd (en): alternative Ă find;
- GNOME Fractal : client Matrix ;
- Libra Core : Cryptomonnaie ;
- Solana: Cryptomonnaie ;
- Discord : logiciel de VoIP[30] et de messagerie instantanée ;
- Hyperledger Sawtooth[31] ;
- Magic Pocket : systÚme de stockage distribué de Dropbox[32];
- Android:
- Gabeldorsche: stack Bluetooth Android (depuis Android 11)[33].
Exemples de code
// This is the main function
fn main() {
// The statements here will be executed when the compiled binary is called
// Print text to the console
println!("Hello World!");
}
use std::mem;
// This function borrows a slice
fn analyze_slice(slice: &[i32]) {
println!("first element of the slice: {}", slice[0]);
println!("the slice has {} elements", slice.len());
}
fn main() {
// Fixed-size array (type signature is superfluous)
let xs: [i32; 5] = [1, 2, 3, 4, 5];
// All elements can be initialized to the same value
let ys: [i32; 500] = [0; 500];
// Indexing starts at 0
println!("first element of the array: {}", xs[0]);
println!("second element of the array: {}", xs[1]);
// `len` returns the size of the array
println!("array size: {}", xs.len());
// Arrays are stack allocated
println!("array occupies {} bytes", mem::size_of_val(&xs));
// Arrays can be automatically borrowed as slices
println!("borrow the whole array as a slice");
analyze_slice(&xs);
// Slices can point to a section of an array
println!("borrow a section of the array as a slice");
analyze_slice(&ys[1..4]);
// Out of bound indexing yields a panic
println!("{}", xs[5]);
}
Bibliographie
Références
- « Announcing Rust 1.70.0 »
- (en) « The Rust Reference », sur rust-lang.org (consulté le ).
- « Chris Lattner's Homepage », sur nondot.org (consulté le ).
- GitHub, (service Internet)
- « https://github.com/rust-lang/rust/blob/master/COPYRIGHT »
- (en) « How Rust went from a side project to the worldâs most-loved programming language », sur MIT Technology Review, (consultĂ© le )
- (en) « The Rust Language », Lambda the Ultimate, (consulté le ).
- (en) « The Rust Programming Language » (consulté le ).
- (en) « Doc language FAQ » (consulté le ).
- (en) « The Rust Programming Language », sur rust-lang.org (consulté le ).
- (en) « The Mozilla Manifesto » (consulté le ).
- (en) Peter Bright, « Samsung teams up with Mozilla to build browser engine for multicore machines », (consulté le ).
- (en) « Rust », sur rust-lang.org (consulté le ).
- (en) « The Rustonomicon », sur rust-lang.org (consulté le ).
- « rust-lang.org/en-US/faq.html#i⊠»(Archive.org ⹠Wikiwix ⹠Archive.is ⹠Google ⹠Que faire ?).
- (en) « Rust vs C clang - Which programs are fastest? », sur pages.debian.net (consulté le ).
- (en) « Project FAQ », (consulté le ).
- (en) « Future Tense », (consultĂ© le ) : « At Mozilla Summit 2010, we launched Rust, a new programming language motivated by safety and concurrency for parallel hardware, the âmanycoreâ future which is upon us. »
- (en) Graydon Hoare, « Rust Progress » [archive du ], (consulté le ).
- (en) Graydon Hoare, « [rust-dev] stage1/rustc builds », (consulté le ) : « After that last change fixing the logging scope context bug, looks like stage1/rustc builds. Just shy of midnight :) ».
- (en) catamorphism, « Mozilla and the Rust community release Rust 0.1 (a strongly-typed systems programming language with a focus on memory safety and concurrency) », (consulté le ).
- (en) Lucian Armasu, « Big Tech Players Start To Adopt The RISC-V Chip Architecture », sur Tom's Hardware,
- « Variables and Mutability - The Rust Programming Language », sur doc.rust-lang.org (consulté le )
- (en) « The Rust Programming Language », sur rust-lang.org (consulté le ).
- « Présentation de Rust sur Léa-Linux » (consulté le ).
- (en) Nicholas Matsakis et Aaron Turon, « Préambule » (consulté le )
- « Sortie de Rust 0.8 » (consulté le ).
- Vincent Hermann, « Microsoft se penche sur le langage Rust pour sa programmation systÚme » (consulté le )
- « Shipping Rust in Firefox â Mozilla Hacks â the Web developer blog », sur hacks.mozilla.org (consultĂ© le )
- « Why Discord is switching from Go to Rust », sur https://blog.discordapp.com/ (consulté le )
- (en-US) Boyd Johnson, « Safety, Performance and Innovation: Rust in Hyperledger Sawtooth », (consulté le )
- (en-US) Cade Metz, « The Epic Story of Dropboxâs Exodus From the Amazon Cloud Empire », WIRED,â (lire en ligne, consultĂ© le )
- (en) « Rust support moves into Android underpinnings », sur ZDNet (consulté le )
Articles connexes
Liens externes
- (en) Site officiel