URL: https://linuxfr.org/news/l-edition-2018-de-rust-est-sortie Title: L’édition 2018 de Rust est sortie ! Authors: Boiethios Xavier Teyssier, Davy Defaud, claudex et patrick_g Date: 2018年12月08日T17:43:36+01:00 License: CC By-SA Tags: rust et lennart_poettering Score: 41 L’édition 2018 du langage [Rust](https://www.rust-lang.org/) est sortie. Cette dépêche est une traduction et un résumé de la [documentation officielle](https://rust-lang-nursery.github.io/edition-guide/rust-2018). Certaines nouveautés ne sont pas si nouvelles, mais c’est toujours utile d’en parler si vous n’avez pas trop suivi l’évolution du langage depuis un an. ---- [Journal à l’origine de la dépêche](https://linuxfr.org/users/boiethios/journaux/l-edition-2018-de-rust-est-sortie) ---- # Édition ? Jamais entendu parler de ça ## Qu’est‐ce qu’une édition en Rust ? Il faut savoir qu’une nouvelle version de Rust sort toutes les six semaines. On peut donc vite être noyé dans les mises à jour et les nouveautés. Afin de pouvoir visualiser les nouveautés avec plus de recul, et surtout ajouter certains *breaking changes*, le langage a (et aura) une nouvelle édition tous les deux ou trois ans. La première édition était celle de 2015, qui regroupe la version 1.0 et une partie des mises à jour qui ont suivi. Cette version 2018 regroupe les dernières nouveautés, dont une partie n’est pas accessible dans l’édition 2015. ## Quid de la stabilité ? Entendre parler de *breaking change* peut faire peur, mais l’équipe de Rust a bien étudié son coup. L’unité de compilation en Rust est le *crate* (caisse en anglais) contrairement au C par exemple, où c’est le fichier. Chacun des _crates_ d’un projet peut utiliser une édition différente du langage. On peut, par exemple, créer un nouveau _crate_ 2015 et utiliser un _crate_ 2018, qui utilise de nouveaux mots-clés sans problèmes. Le langage peut donc avoir des changements incompatibles sans que le code des utilisateurs n’en soit impacté. En revanche, la bibliothèque standard ne peut en aucun cas avoir de *breaking changes*, puisque si l’on change un type entre les éditions 2015 à 2018, par exemple, on ne peut plus passer une instance de ce type d’un _crate_ à l’autre. ## Comment utiliser cette édition ? Dans les faits, cela signifie que : - si vous voulez créer un nouveau projet, la commande `cargo new` créera un nouveau projet directement avec l’édition 2018 ; concrètement, une nouvelle ligne est ajoutée dans le manifeste : `edition = "2018"` ; - si vous voulez mettre à jour un projet existant, il suffit de lancer la commande `cargo fix --edition`, ce qui va rendre les sources compatibles avec la dernière version. # Quelles sont les nouveautés ? Venons‐en au fait : quoi de neuf ? ## Le système de modules ### Les chemins Le système de modules de la première édition était l’un des éléments difficiles à appréhender pour un débutant. Par exemple, selon qu’on utilisait un chemin dans une instruction `use` ou dans le code, ça pouvait compiler ou pas : le système de chemins pouvait sembler quelque peu incohérent. Dans l’édition 2018, tout est harmonisé : dans tous les cas, il faut utiliser `crate` comme racine du chemin pour se référer au module de base, et `self` pour le module courant. L’équipe de Rust est encore en débat pour savoir lequel des deux sera le comportement par défaut en cas de chemin relatif. ### Les dépendances externes La directive `extern crate` n’est plus nécessaire quand on veut utiliser une dépendance externe. Effectivement, ça faisait doublon puisque l’information est dans tous les cas dans le manifeste du projet `Cargo.toml`. ### Les macros Les macros, qu’elles soient procédurales ou non, ressemblent de plus en plus aux autres items du langage. On les importe maintenant dans le *scope* avec `use`, tout comme le reste : `use mon::chemin::ma_macro;`. Il y a encore du travail à faire sur les macros (hygiène, macros 2.0...), mais elles pourront bientôt s’utiliser à tout point de vue comme des fonctions. ### Quel fichier pour quel module Maintenant, quand on veut mettre un sous‐module dans un dossier, on n’a plus besoin du fichier `mod.rs`. Avant : ``` | |- foo | |- mod.rs | |- autre_module.rs ``` Après : ``` | |- foo.rs |- foo | |- autre_module.rs ``` Ça permet de ne pas avoir tout un tas de fichiers `mod.rs` ouverts en même temps dans un éditeur (c’est vrai qu’on s’y perdait vite dans les gros projets). ### De nouveaux types de modifieurs de visibilité On peut mettre tout un tas de paramètres au mot clef `pub` pour un comportement un peu similaire au mot-clef `friend` en C++ : `pub(crate)`, `pub(a::b::c)`, etc. Il y a plus de détails dans la documentation du langage. ### Directive `use` imbriquée L’utilisation de `use` est plus souple. On peut maintenant marquer : ```rust use std::{ fs::File, io::Read, path::{ Path, PathBuf } }; ``` ## Système de trait Maintenant, il existe une syntaxe plus explicite et symétrique pour la répartition dynamique *vs* statique : ```rust // Géré à la compilation via monomorphisation : fn foo(it: impl Iterator) { for i in it { // ... } } ``` ```rust // Géré pendant le runtime : fn foo(it: Box>) { for i in it { // ... } } ``` La notation `impl` peut remplacer l’introduction d’un type générique (sauf dans les cas les plus complexes) en simplifiant la syntaxe. Ainsi, on aurait pu écrire : ```rust fn foo(it: T) where T: Iterator { for i in it { // ... } } ``` ## Simplification concernant les types « lifetime » ### Lifetimes non lexicaux Il y aurait beaucoup à dire à ce sujet, mais le *borrow‐checker* (la partie du compilateur qui vérifie la sécurité du code) a été réimplémenté avec un algorithme différent. Il est maintenant plus souple, dans le sens ou il élimine plus de faux‐positifs. Le précédent *borrow‐checker* refusait certaines choses qui auraient dû être acceptées, par exemple : ```rust let mut v = vec![1, 2, 3]; v.push(v.len()) ``` ### Pattern matching plus intelligent Le *pattern matching* n’oblige plus à faire des contorsions quand on met des références en correspondance. Par exemple, avant on devait écrire : ```rust let s: &Option = &Some("hello".to_string()); match s { // On match s avec `&Some(_)` puisque s est une référence, // et pour la valeur interne on doit marquer `ref s` // puisqu'on ne peut pas déplacer le contenu d'une donnée // qui a été empruntée. &Some(ref s) => println!("s is: {}", s), _ => (), }; ``` En 2018, le code est plus simple, et plus intuitif : ```rust let s: &Option = &Some("hello".to_string()); match s { // Le compilateur comprend qu'on veut récupérer une référence // sur la valeur interne. La référence est pour ainsi dire passée // de l'option à la valeur interne. Some(s) => println!("s is: {}", s), _ => (), }; ``` ### Simplification dans l’écriture des « lifetimes » génériques Il s’agit d’un certain nombre de cas de figure où l’écriture était inutile, redondante, etc. En vrac : - tout comme les types de données, les types *lifetime* ont un identificateur anonyme `'_` ; on l’utilise dans le cas d’un type avec un *lifetime* générique : `Foo<'_>` ; - plus besoin de spécifier le *lifetime* générique quand on implémente un _trait_ pour une référence : `impl Trait for &Foo { /* etc. */ }` ; - plus besoin de spécifier l’interdépendance des types de données génériques avec les types *lifetime* génériques dans le cas d’une `struct`, par exemple : `T: 'a`. ## Autres Voici une liste de fonctionnalités plus complexes, et donc que je détaillerai moins : ### Notations pour le code asynchrone Il sera plus simple d’écrire du code asynchrone grâce aux mots‐clefs `async` et `await`. Cette fonctionnalité n’est pas encore stable, mais les mots‐clefs sont réservés pour l’édition 2018. ### SIMD, 128 bits Sont pris en charge officiellement les opérations SIMD et les types entiers sur 128 bits `i128` et `u128`. ### Macros procédurales Tous les types de macros procédurales ont été stabilisés pour 2018 : - les instructions `derive` : `#[derive(Foo)]` ; - les attributs : `#[foo(/* etc. */)]` ; - les macros appelées comme des fonctions : `foo!(/* etc. */)`. ### Zéro ou une occurrence dans les macros Maintenant, les mêmes répéteurs que pour les expressions rationnelles existent dans l’implémentation des macros : - `*` pour zéro répétition ou plus ; - `+` pour une répétition ou plus ; - `?` pour zéro ou une occurrence. ### On peut faire du « pattern-matching » avec des slices ```rust fn main() { bonjour(&[]); // sortie : Eh, il n'y a personne ici. bonjour(&["Linus"]); // sortie : Coucou, Linus, j'ai l'impression que tu es tout seul. bonjour(&["Linus", "Richard"]); // sortie : Coucou, Linus et Richard. Content de voir que vous êtes au moins deux ! bonjour(&["Linus", "Richard", "Lennart"]); // sortie : Bonjour à tous, j'ai l'impression que nous sommes trois aujourd'hui. } fn bonjour(people: &[&str]) { match people { [] => println!("Eh, il n'y a personne ici."), [tout_seul] => println!("Coucou, {}, j'ai l'impression que tu es tout seul.", tout_seul), [premier, second] => println!("Coucou, {} et {}. \ Content de voir que vous êtes au moins 2 !", premier, second), _ => println!("Bonjour à tous, j'ai l'impression que nous sommes {} aujourd'hui.", people.len()), } } ``` # Conclusion Le langage a beaucoup avancé au cours des trois dernières années, avec de nouvelles fonctionnalités excitantes et des simplifications bienvenues. Je ne peux que vous conseiller de lire la documentation officielle à ce sujet. La communauté attend encore avec impatience bien d’autres choses (je vous invite à voir la liste des RFC pour vous faire une idée) et je suis convaincu que la prochaine édition sera tout aussi riche en nouveautés que celle‐ci.

AltStyle によって変換されたページ (->オリジナル) /