Rust (langage)

langage de programmation informatique

Rust
Logo.
Image illustrative de l’article Rust (langage)

Date de première version Voir et modifier les données sur Wikidata
Paradigmes Impératif, fonctionnel, concurrent
Auteur Graydon Hoare
Développeurs Mozilla
Dernière version 1.55.0 ()[1]Voir et modifier les données sur Wikidata
Typage Fort, statique
Influencé par C++, Erlang, Haskell, Scala, OCaml, Scheme, Swift, C#, Alef, Limbo[2]
Écrit en RustVoir et modifier les données sur Wikidata
Système d'exploitation Multiplate-forme
Licence Licence Apache version 2.0[3] et licence MIT[3]Voir et modifier les données sur Wikidata
Site web www.rust-lang.orgVoir et modifier les données sur Wikidata
Extension de fichier rs et rlibVoir et modifier les données sur Wikidata

Rust est un langage de programmation compilé multi-paradigme conçu et développé par Mozilla Research depuis 2010[4]. Il a été conçu pour être « un langage fiable, concurrent, pratique »[5],[6], supportant les styles de programmation purement fonctionnel, modèle d'acteur, procédural, ainsi qu'orienté objet sous certains aspects[7].

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[8], 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[9] 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é.

DescriptionModifier

Rust se veut un langage performant, sûr et productif[10].

Le langage peut notamment donner des garanties d'absence d'erreur de segmentation ou de situation de concurrence[11] dès l'étape de compilation. De plus, ceci se fait sans ramasse-miettes[12]. Ses performances sont comparables à celles de C ou C++[13] 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.

HistoireModifier

Le langage s'est développé à partir d'un projet personnel de Graydon Hoare, qui commença à travailler dessus en 2006. Son employeur, Mozilla, commença sa participation en 2009[14] et révéla officiellement ses travaux en 2010[15]. La même année, le projet passa du compilateur initialement utilisé (écrit en OCaml) au compilateur auto-hébergé écrit en Rust[16]. Ce compilateur, connu sous le nom de rustc, s'est compilé avec succès en 2011[17]. Le compilateur auto-hébergé utilise LLVM pour son backend.

La première version alpha numérotée du compilateur Rust apparait en janvier 2012[18].

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[19].

CaractéristiquesModifier

SyntaxeModifier

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éModifier

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[20].

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 motifModifier

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[21].

Métaprogrammation permise grâce au filtrage par motifModifier

Programmation génériqueModifier

Afin de garantir la programmation générique, le langage Rust implément 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, toutes les listes de valeurs supportant l’opérateur de comparaison < pourra être passé comme argument pour cette fonction.

Définition des méthodes communes par le biais de traitModifier

Les traits sont assimilables aux interfaces en java ou les 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 empruntModifier

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 fortsModifier

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[22][réf. obsolète][23]. Ces concepts ont été empruntés à des langages de programmation existants et assemblés dans un seul langage[24] :

Rust est souvent décrit comme l'un des successeurs potentiels de C et C++[25] (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 RustModifier

Exemples de codeModifier

Hello world :

// 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!");
}

Arrays and Slices :

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]);
}

AnnexesModifier

Articles connexesModifier

Liens externesModifier

RéférencesModifier

  1. « Announcing Rust 1.55.0 »
  2. https://doc.rust-lang.org/stable/reference/influences.html
  3. a et b « https://github.com/rust-lang/rust/blob/master/COPYRIGHT »
  4. (en) « The Rust Language », Lambda the Ultimate, (consulté le ).
  5. (en) « The Rust Programming Language » (consulté le ).
  6. (en) « Doc language FAQ » (consulté le ).
  7. https://doc.rust-lang.org/book/ch17-01-what-is-oo.html
  8. (en) « The Mozilla Manifesto » (consulté le ).
  9. (en) Peter Bright, « Samsung teams up with Mozilla to build browser engine for multicore machines », (consulté le ).
  10. https://www.rust-lang.org/
  11. https://doc.rust-lang.org/nomicon/what-unsafe-does.html
  12. https://www.rust-lang.org/en-US/faq.html#is-rust-garbage-collected
  13. https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/rust.html
  14. (en) « Project FAQ », (consulté le ).
  15. (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. »
  16. (en) Graydon Hoare, « Rust Progress » [archive du ], (consulté le ).
  17. (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 :) ».
  18. (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 ).
  19. (en) Lucian Armasu, « Big Tech Players Start To Adopt The RISC-V Chip Architecture », sur Tom's Hardware,
  20. « Variables and Mutability - The Rust Programming Language », sur doc.rust-lang.org (consulté le )
  21. https://doc.rust-lang.org/book/second-edition/ch06-01-defining-an-enum.html#the-option-enum-and-its-advantages-over-null-values
  22. « Présentation de Rust sur Léa-Linux » (consulté le ).
  23. (en) Nicholas Matsakis et Aaron Turon, « Préambule » (consulté le )
  24. « Sortie de Rust 0.8 » (consulté le ).
  25. Vincent Hermann, « Microsoft se penche sur le langage Rust pour sa programmation système » (consulté le )
  26. « Shipping Rust in Firefox ★ Mozilla Hacks – the Web developer blog », sur hacks.mozilla.org (consulté le )
  27. (en) « Rust NPM whitepaper », The Rust Project Developers,‎ (lire en ligne)
  28. « Why Discord is switching from Go to Rust », sur https://blog.discordapp.com/ (consulté le )
  29. (en-US) Boyd Johnson, « Safety, Performance and Innovation: Rust in Hyperledger Sawtooth », (consulté le )
  30. (en-US) Cade Metz, « The Epic Story of Dropbox’s Exodus From the Amazon Cloud Empire », WIRED,‎ (lire en ligne, consulté le )