Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Rust 1.40 est disponible. Cette version s'accompagne de #[non_exhaustive]
Et apporte des améliorations à macros!() et #[attribute]

Le , par Stéphane le calme

76PARTAGES

5  0 
#[non_exhaustive] structures, énumérations et variantes

Supposons que vous êtes l'auteur d'une bibliothèque crate alpha, qui a une pub struct Foo. Vous souhaitez également rendre les champs alpha::Foo pub, mais vous ne savez pas si vous ajouterez peut-être d'autres champs à Foo dans les prochaines versions. Alors maintenant, vous avez un dilemme : soit vous rendez les champs privés, avec les inconvénients qui suivent, soit vous risquez les dépendances utilisateurs en fonction des champs exacts, brisant leur code lorsque vous en ajoutez un nouveau. Rust 1.40.0 introduit un moyen de venir à bout de ce dilemme : #[non_exhaustive].

L'attribut #[non_exhaustive], lorsqu'il est attaché à une struct ou à la variante d'une enum, empêchera le code en dehors de la crate le définissant de construire ladite struct ou variante. Pour éviter les bris futurs, les autres crate sont également empêchées de correspondre de manière exhaustive sur les champs. L'exemple suivant illustre les erreurs en beta qui dépendent de alpha:

Code Rust : Sélectionner tout
1
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
// alpha/lib.rs: 
  
#[non_exhaustive] 
struct Foo { 
    pub a: bool, 
} 
  
enum Bar { 
    #[non_exhaustive] 
    Variant { b: u8 } 
} 
  
fn make_foo() -> Foo { ... } 
fn make_bar() -> Bar { ... } 
  
// beta/lib.rs: 
  
let x = Foo { a: true }; //~ ERROR 
let Foo { a } = make_foo(); //~ ERROR 
  
// `beta` will still compile when more fields are added. 
let Foo { a, .. } = make_foo(); //~ OK 
  
  
let x = Bar::Variant { b: 42 }; //~ ERROR 
let Bar::Variant { b } = make_bar(); //~ ERROR 
let Bar::Variant { b, .. } = make_bar(); //~ OK 
                   // -- `beta` will still compile...

Ce qui se passe dans les coulisses est que la visibilité des constructeurs pour une variante #[non_exhaustive], struct ou enum est réduite à pub(crate), empêchant l'accès à l'extérieur de la crate le définissant.

Un aspect peut-être plus important de #[non_exhaustive] est qu'il peut également être attaché aux enum elles-mêmes. Un exemple, tiré de la bibliothèque standard, est Ordering:

Code Rust : Sélectionner tout
1
2
#[non_exhaustive] 
pub enum Ordering { Relaxed, Release, Acquire, AcqRel, SeqCst }

Le but de #[non_exhaustive] dans ce contexte est de garantir que plus de variantes peuvent être ajoutées au fil du temps. Ceci est réalisé en empêchant d'autres crate de correspondre de manière exhaustive au modèle match sur Ordering. Autrement dit, le compilateur rejetterait:

Code Rust : Sélectionner tout
1
2
3
4
5
6
7
match ordering { 
    // This is an error, since if a new variant is added, 
    // this would suddenly break on an upgrade of the compiler. 
    Relaxed | Release | Acquire | AcqRel | SeqCst => { 
        /* logic */ 
    } 
}

Au lieu de cela, d'autres crate doivent tenir compte de la possibilité de plus de variantes en ajoutant un bras générique en utilisant par exemple _:

Code Rust : Sélectionner tout
1
2
3
4
5
match ordering { 
    Relaxed | Release | Acquire | AcqRel | SeqCst => { /* ... */ } 
    // OK; if more variants are added, nothing will break. 
    _ => { /* logic */ } 
}

Améliorations des macros et des attributs

Dans la version 1.40.0, l'équipe a apporté plusieurs améliorations aux macros et aux attributs, notamment:
  • Appel des macros procédurales mac!() Dans des contextes de type.

    Par exemple, vous pouvez écrire type Foo = expand_to_type!(bar);expand_to_type serait une macro procédurale.
  • Macros dans des blocs extern { ... }.

    Cela inclut les macros bang! (), Par exemple:

    Code Rust : Sélectionner tout
    1
    2
    3
    4
    5
    6
    macro_rules! make_item { ($name:ident) => { fn $name(); } } 
      
    extern { 
        make_item!(alpha); 
        make_item!(beta); 
    }
  • Les attributs de macro procéduraux sur les éléments des blocs extern {...} sont désormais également pris en charge:

    Code Rust : Sélectionner tout
    1
    2
    3
    4
    5
    extern "C" { 
        // Let's assume that this expands to `fn foo();`. 
        #[my_identity_macro] 
        fn foo(); 
    }
  • Génération d'éléments macro_rules! dans les macros procédurales.

    Les macros fonctionnelles (mac!()) et leurs attributs (# [mac]) peuvent maintenant générer des éléments macro_rules!.
  • Le matcher $m:meta prend en charge des valeurs de flux de jetons arbitraires.

    Autrement dit, ce qui suit est maintenant valide :

    Code Rust : Sélectionner tout
    1
    2
    3
    4
    5
    6
    macro_rules! accept_meta { ($m:meta) => {} } 
    accept_meta!( my::path ); 
    accept_meta!( my::path = "lit" ); 
    accept_meta!( my::path ( a b c ) ); 
    accept_meta!( my::path [ a b c ] ); 
    accept_meta!( my::path { a b c } );


Vérificateur d'emprunt

Les contextes jouent un rôle important dans le fonctionnement du système de propriété (ownership), d'emprunts (borrowing) et de durées de vie (lifetime). Ils témoignent de la validité (ou non) d'un emprunt, indiquent au compilateur lorsqu'une ressource peut être libérée et lorsqu'une variable est créée ou détruite. Très souvent, nous souhaiterions accéder à une ressource sans en prendre possession. Pour ce faire, Rust utilise un système d'emprunts. Plutôt que de passer un objet par valeur (T), il peut être passé par référence (&T). Le compilateur garantit (grâce au vérificateur d'emprunts) que les références sont toujours valides. Tant qu'une référence de l'objet existe, il ne sera pas détruit.

Dans la version 1.35.0, l'équipe a annoncé que NLL était arrivé à Rust 2015 après avoir été publié pour la première fois pour l'édition 2018 dans Rust 1.31. Comme elle l'avait noté à l'époque, l'ancien vérificateur d'emprunt avait quelques bogues qui conduisaient à une mémoire insécurisée, et le vérificateur d'emprunt NLL les a corrigés. Comme ces correctifs cassent parfois du code stable, l'équipe a décidé de supprimer progressivement les erreurs, en vérifiant si l'ancien vérificateur d'emprunt accepterait le programme et le vérificateur NLL le rejetterait. Dans ces cas, les erreurs seraient rétrogradées en avertissements.

La version précédente, Rust 1.39.0, transforme ces avertissements en erreurs pour le code à l'aide de l'édition 2018. Rust 1.40.0 applique le même changement pour les utilisateurs de l'édition 2015, fermant définitivement ces failles. Cela nous permet également de nettoyer l'ancien code du compilateur.

Ajouts à la bibliothèque standard

Dans Rust 1.40.0, les fonctions et macros suivantes ont été stabilisées:
  • todo!() une macro, qui est une version plus courte, plus mémorable et plus pratique de unimplemented!().
  • slice::repeat xrée un Vec en répétant une tranche n fois.
  • mem::take cette fonction prend la valeur d'une référence mutable et la remplace par la valeur par défaut du type. Ceci est similaire à Option::take et à Cell::take et fournit un raccourci pratique pour mem::replace(&mut dst, Default::default())
  • BTreeMap::get_key_value et HashMap::get_key_value renvoie la paire clé-valeur correspondant à la clé fournie.
  • Option::as_deref, Option::as_deref_mut ceux-ci fonctionnent de manière similaire à Option::as_ref et Option::as_mut mais utilisent également Deref et DerefMut respectivement, de sorte que opt_box.as_deref () et opt_box.as_deref_mut (), où opt_box: Option >, produisent une Option< & T> et Option <& mut T> respectivement.
  • UdpSocket :: peer_addr renvoie l'adresse du socket de l'homologue distant auquel ce socket était connecté.
  • {f32,f64}::to_be_bytes, {f32,f64}::to_le_bytes, f32,f64}::to_ne_bytes, {f32,f64}::from_be_bytes,

Source : Rust

Une erreur dans cette actualité ? Signalez-le nous !