IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Communiqués de presse

    Homme Profil pro
    Rédacteur technique
    Inscrit en
    Avril 2025
    Messages
    460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Rédacteur technique

    Informations forums :
    Inscription : Avril 2025
    Messages : 460
    Par défaut Rust 1.92 active les tables de déroulement par défaut et affine les vérifications de lints et d'attributs
    La version 1.92 du langage de programmation Rust est disponible, introduisant les tables de déroulement par défaut et affinant les vérifications de lints et d'attributs

    Rust 1.92 est la dernière version du langage de programmation polyvalent, qui introduit des changements importants pour les développeurs. Notamment, les tables de déroulement sont désormais émises par défaut même lorsque le drapeau -Cpanic=abort est activé, ce qui garantit le bon fonctionnement des traces de retour en arrière dans les scénarios de panique-abandon. Les utilisateurs qui ne souhaitent pas utiliser les tables de déroulement peuvent désormais les désactiver explicitement avec -Cforce-unwind-tables=no.

    Rust est un langage de programmation compilé multi-paradigme qui met l'accent sur la performance, la sûreté des types et la concurrence. Il assure la sécurité mémoire, ce qui signifie que toutes les références pointent vers une mémoire valide, sans nécessiter l'utilisation de techniques de gestion de la mémoire automatisée telles que le ramasse-miettes. Afin d'assurer la sécurité de la mémoire et d'empêcher une situation de compétition aux données, son « vérificateur d'emprunts » suit la durée de vie des objets de toutes les références dans un programme à la compilation. Rust a été remarqué pour son adoption rapide, selon le Stack Overflow Survey 2025, c'est le langage le plus apprécié dans ce sondage.

    Rust 1.92 est la dernière version du langage de programmation polyvalent, qui introduit des changements importants pour les développeurs. Notamment, les tables de déroulement sont désormais émises par défaut même lorsque le drapeau -Cpanic=abort est activé, ce qui garantit le bon fonctionnement des traces de retour en arrière dans les scénarios de panique-abandon. Les utilisateurs qui ne souhaitent pas utiliser les tables de déroulement peuvent désormais les désactiver explicitement avec -Cforce-unwind-tables=no.

    Suite aux améliorations continues apportées au système de types, cette version poursuit les efforts de stabilisation du type never. Deux futurs lints de compatibilité, never_type_fallback_flowing_into_unsafe et dependency_on_unit_never_type_fallback, sont désormais refusés par défaut, ce qui signifie que toute violation entraînera des erreurs de compilation plutôt que des avertissements ou une autorisation silencieuse.

    En ce qui concerne les lints, le lint unused_must_use n'émet plus d'avertissement lorsque les fonctions renvoient un Result ou un ControlFlow, tel que Result. Cela reflète le fait que ces types d'erreurs ne peuvent pas réellement se produire, ce qui réduit les vérifications inutiles pendant le développement.

    Ces mises à jour fondamentales s'accompagnent de modifications importantes dans la gestion des attributs intégrés à Rust. Le compilateur traite désormais les attributs de manière plus stricte et génère des diagnostics plus clairs et plus cohérents. Par exemple, les arguments macro_export incorrects dans les dépendances sont désormais considérés comme des lints refusés par défaut, ce qui rend ces problèmes plus visibles lors de la compilation.

    Nom : 1.jpg
Affichages : 20396
Taille : 18,4 Ko

    Voici les principales mises à jour de la version 1.92.0 :

    Lignes de code never type refusées par défaut

    Les équipes chargées du langage et du compilateur continuent de travailler à la stabilisation du type never. Dans cette version, les lignes de code never_type_fallback_flowing_into_unsafe et dependency_on_unit_never_type_fallback, compatibles avec les versions futures, ont été refusées par défaut, ce qui signifie qu'elles provoqueront une erreur de compilation lorsqu'elles seront détectées.

    Il convient de noter que même si cela peut entraîner des erreurs de compilation, il s'agit toujours d'un lint ; ces lints peuvent tous être #[allow]. Ces lints ne se déclencheront également que lors de la compilation directe des crates concernées, et non lorsqu'elles sont compilées en tant que dépendances (bien qu'un avertissement soit signalé par Cargo dans de tels cas).

    Ces lints détectent le code susceptible d'être rompu par la stabilisation du type never. Il est fortement recommandé de les corriger s'ils sont signalés dans votre graphique de crate.

    Nous estimons qu'environ 500 crates sont concernées par ce lint. Malgré cela, nous pensons que cela est acceptable, car les lints ne constituent pas un changement radical et permettront de stabiliser le type never à l'avenir.


    unused_must_use n'affiche plus d'avertissement concernant Result<(), UninhabitedType>

    Le lint unused_must_use de Rust affiche un avertissement lorsque la valeur de retour d'une fonction est ignorée, si la fonction ou son type de retour est annoté avec #[must_use]. Par exemple, cela avertit si vous ignorez un type de retour de Result, pour vous rappeler d'utiliser ?, ou quelque chose comme .expect(« ... »).

    Cependant, certaines fonctions renvoient Result, mais le type d'erreur qu'elles utilisent n'est en fait pas « habité », ce qui signifie que vous ne pouvez construire aucune valeur de ce type (par exemple, les types ! ou Infallible).

    Le lint unused_must_use n'affiche désormais plus d'avertissement sur Result<(), UninhabitedType> ou sur ControlFlow<UninhabitedType, ()>. Par exemple, il n'affichera pas d'avertissement sur Result<(), Infallible>. Cela évite d'avoir à vérifier une erreur qui ne peut jamais se produire.

    Code Rust : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    use core::convert::Infallible;
    fn can_never_fail() -> Result<(), Infallible> {
        // ...
        Ok(())
    }
     
    fn main() {
        can_never_fail();
    }



    Ceci est particulièrement utile avec le modèle courant d'un trait associé à un type d'erreur, où le type d'erreur peut parfois être infaillible :

    Code Rust : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    trait UsesAssocErrorType {
        type Error;
        fn method(&self) -> Result<(), Self::Error>;
    }
     
    struct CannotFail;
    impl UsesAssocErrorType for CannotFail {
        type Error = core::convert::Infallible;
        fn method(&self) -> Result<(), Self::Error> {
            Ok(())
        }
    }
     
    struct CanFail;
    impl UsesAssocErrorType for CanFail {
        type Error = std::io::Error;
        fn method(&self) -> Result<(), Self::Error> {
            Err(std::io::Error::other("something went wrong"))
        }
    }
     
    fn main() {
        CannotFail.method(); // No warning
        CanFail.method(); // Warning: unused `Result` that must be used
    }



    Émettre des tables de déroulement même lorsque -Cpanic=abort est activé sous Linux

    Les backtraces avec -Cpanic=abort fonctionnaient auparavant dans Rust 1.22, mais ne fonctionnaient plus dans Rust 1.23, car nous avons cessé d'émettre des tables de déroulement avec -Cpanic=abort. Dans Rust 1.45, une solution de contournement sous la forme de -Cforce-unwind-tables=yes a été stabilisée.

    Dans Rust 1.92, les tables de déroulement seront émises par défaut même lorsque -Cpanic=abort est spécifié, ce qui permettra aux backtraces de fonctionner correctement. Si les tables de déroulement ne sont pas souhaitées, les utilisateurs doivent utiliser -Cforce-unwind-tables=no pour désactiver explicitement leur émission.

    Valider l'entrée vers #[macro_export]

    Au cours des dernières versions, de nombreux changements ont été apportés à la manière dont les attributs intégrés sont traités dans le compilateur. Cela devrait considérablement améliorer les messages d'erreur et les avertissements fournis par Rust pour les attributs intégrés et, surtout, rendre ces diagnostics plus cohérents parmi les plus de 100 attributs intégrés.

    Pour donner un petit exemple, dans cette version en particulier, Rust est devenu plus strict dans la vérification des arguments autorisés pour macro_export en mettant à niveau cette vérification vers un « lint par défaut refusé » qui sera signalé dans les dépendances.

    Source : Annonce Rust 1.92

    Et vous ?

    Pensez-vous que cette mise à jour est crédible ou pertinente ?
    Quel est votre avis sur le sujet ?

    Voir aussi :

    La version 1.91 du langage de programmation Rust est disponible, ajoutant la prise en charge de niveau 1 pour la plateforme Windows ARM64, les avertissements de pointeurs bruts, et plus encore

    La fondation Rust annonce la création d'un fonds pour les mainteneurs afin d'assurer la continuité et de soutenir les rôles à long terme des développeurs qui rendent possible le langage de programmation Rust

    C'est désormais officiel : Rust dans le noyau Linux sort du cadre expérimental. Le Rust vient de faire l'objet d'intégration comme partie essentielle du kernel aux côtés du toujours présent langage C
    Publication de communiqués de presse en informatique. Contribuez au club : corrections, suggestions, critiques, ... Contactez le service news et Rédigez des actualités

  2. #2
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    551
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 551
    Par défaut Là j'ai du mal a comprendre...
    à tous,

    Je ne suis pas un expert en Rust, mais j'ai du mal a comprendre l'intérêt du type ! (never), que je viens de découvrir via le post précédent.

    De ce que je comprend, il est là pour "signaler" qu'une fonction ne retourne pas de valeur. Mais, pourquoi faut-il signaler au compilateur qu'une fonction ne retourne pas de valeur ? SI une fonction n'a pas a retourner de valeur, le compilateur pourrait le détecter car il n'y aurait dans ce cas pas de 'return' dans le code de la fonction ? (ou de yield dans le cas d'une coroutine).

    En pascal, on différence cela via une procédure (qui ne retourne pas de valeur), et une fonction qui elle retourne une valeur (je ne sais plus si elle DOIT ou si elle PEUT) retourner une valeur.

    De même, le compilateur pourrait refuser de compiler du code qui ne récupère pas la/les valeurs de retour, non ? N'a-t-il pas toutes les informations nécessaires pour le faire ?

    J'ai tenté quelques recherches, mais je n'ai pas été convaincu de son intérêt (ou, ce qui est fort possible, que je ne l'ai pas compris, soit parce que l'explication n'était pas clair, soit parce que l'explication était trop compliquée pour moi).

    Merci d'avance pour vos lumières.

    BàV et Peace & Love.

  3. #3
    Membre éclairé
    Homme Profil pro
    Développeur Java
    Inscrit en
    Janvier 2007
    Messages
    257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Janvier 2007
    Messages : 257
    Par défaut
    En réalité ! signifie qu'à la fin de la function, le code ne va pas reprendre chez l'appelant.

    Un exemple est la fonction std::process::exit ( https://doc.rust-lang.org/std/process/fn.exit.html ) dont la signature est pub fn exit(code: i32) -> !

    Si tu as un code tel que :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    fn main() {
        println!("hello");
        std::process::exit(0);
        println!("world");
    }
    World ne sera pas exécuté car la fonction exit ne reviendra jamais dans la fonction main.

    Cela permet de faciliter les analyse de code pour déterminer que le code suivant cette appel de function ne sera jamais exécuté.
    Ça permet également d'éviter de devoir faire un exit(0) suivi d'un return <quelque chose> si l'on veut quitter le programme directement depuis une fonction.

    Une fonction ne renvoyant pas de valeur fn ma_fonction() {} correspond en réalité à fn ma_fonction() -> () {}
    Dans ce cas, à la fin de l'exécution de cette fonction, le flux de code continuera à partir de la prochaine instruction dans la fonction appellante.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    fn main() {
        println!("hello");
        ma_fonction();
        println!("world");
    }
    Ici hello et world seront bien afficher dans la console.

    Désolé pour le formatage de mon message. Je n'ai pas trouvé comment faire sur téléphone.

  4. #4
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    551
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 551
    Par défaut Merci, mais
    Citation Envoyé par gta126 Voir le message
    En réalité ! signifie qu'à la fin de la function, le code ne va pas reprendre chez l'appelant.

    Un exemple est la fonction std::process::exit ( https://doc.rust-lang.org/std/process/fn.exit.html ) dont la signature est pub fn exit(code: i32) -> !

    Si tu as un code tel que :
    fn main() {
    println!("hello");
    std::process::exit(0);
    println!("world");
    }

    World ne sera pas exécuté car la fonction exit ne reviendra jamais dans la fonction main.
    Ok, mais dans ce cas, pourquoi ne pas faire un simple 'return' ou 'exit'.

    c'est un peut comme si je fais en 'C'

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    fct() {
        code...
        return
        code...
    }
    Le compilateur peut détecter que ce qui se trouve après le 'return' ne sera pas exécuté, non ?

    Citation Envoyé par gta126 Voir le message
    Cela permet de faciliter les analyse de code pour déterminer que le code suivant cette appel de function ne sera jamais exécuté.
    Ça permet également d'éviter de devoir faire un exit(0) suivi d'un return <quelque chose> si l'on veut quitter le programme directement depuis une fonction.

    Une fonction ne renvoyant pas de valeur fn ma_fonction() {} correspond en réalité à fn ma_fonction() -> () {}
    Dans ce cas, à la fin de l'exécution de cette fonction, le flux de code continuera à partir de la prochaine instruction dans la fonction appellante.

    fn main() {
    println!("hello");
    ma_fonction();
    println!("world");
    }

    Ici hello et world seront bien afficher dans la console.

    Désolé pour le formatage de mon messgae. Je n'ai pas trouvé comment faire sur téléphone.
    Merci de la réponse, mais je trouve (ce n'est que mon avis), qu'on utilise un canon pour tuer une mouche...

    Ou alors il y a là quelque chose de plus profond dont je ne capte pas l'usage ou l'utilité.

  5. #5
    Membre Expert
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    881
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 881
    Par défaut
    Le ! est un type qui indique que le programme s'arrêtera dans cette fonction. Ça permet de le savoir dès la lecture du code ou de la doc sans avoir besoin de lire le code de la fonction.

    Donc non, pas comme en C/C++ et consorts. C'est un confort en somme.

  6. #6
    Membre Expert Avatar de Uther
    Homme Profil pro
    Tourneur Fraiseur
    Inscrit en
    Avril 2002
    Messages
    4 748
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Tourneur Fraiseur

    Informations forums :
    Inscription : Avril 2002
    Messages : 4 748
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    De ce que je comprend, il est là pour "signaler" qu'une fonction ne retourne pas de valeur. Mais, pourquoi faut-il signaler au compilateur qu'une fonction ne retourne pas de valeur ? SI une fonction n'a pas a retourner de valeur, le compilateur pourrait le détecter car il n'y aurait dans ce cas pas de 'return' dans le code de la fonction ? (ou de yield dans le cas d'une coroutine).
    Une fonction qui retourne ! fait bien plus que ne pas retourner de valeur : elle indique explicitement qu'elle ne se terminera jamais, soit parce qu'elle boucle indéfiniment, soit parce qu'elle termine le fil d’exécution (panique ou exit). Le point d’exclamation n'est pas pour le moment un type a part entière (il n'est utilisable qu'en retour de fonction), mais le but est qu'il devienne le moyen standard de représenter un type qui ne peut exister. Actuellement on utilise pour ça les énumération vides qui partagent certains points commun, notamment le fait de ne pas pouvoir être instanciées.

    Ces types ont diverses particularités permises par le fait que leur utilisation concrète est impossible. Par exemple le type ! est automatiquement convertissable en n'importe quel autre type. Ca a de vrai usages sans qu'on s'en rende forcément compte.
    Le code suivant ne fonctionnerait pas si la macro panic qui interrompt l’exécution ne retournait pas !, car tous les éléments du match sont censés être du même type.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
        let texte = match chiffre {
            1 => "un",
            2 => "deux",
            _ => panic!("nombre non supporté"),
        };
    Un autre particularité est que les variant d'une énumération contenant un type non instanciable ne sont pas utilisables non plus, donc qu'il n'y a pas besoin de les traiter dans les cas de pattern matching exhaustif:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
        let nombre: Result<u32,!> = Ok(10);
        let Ok(x) = nombre;  // pas besoin de gérer le cas Err car le type `!` le rend impossible.

    Citation Envoyé par OuftiBoy Voir le message
    En pascal, on différence cela via une procédure (qui ne retourne pas de valeur), et une fonction qui elle retourne une valeur (je ne sais plus si elle DOIT ou si elle PEUT) retourner une valeur.
    En Rust comme en C, il n'y a pas de différenciation entre fonctions et procédures au niveau de la syntaxe, mais il y a bien des fonctions sans type de retour spécifié. En fait ces fonctions retournent implicitement le type unité : (). Ce type unité ne contient aucune valeur (il a une taille de zéro octets), mais il reste et instanciable, contrairement au type ! et aux énumération vides.

  7. #7
    Membre confirmé
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2021
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mai 2021
    Messages : 123
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    Merci de la réponse, mais je trouve (ce n'est que mon avis), qu'on utilise un canon pour tuer une mouche...

    Ou alors il y a là quelque chose de plus profond dont je ne capte pas l'usage ou l'utilité.
    D'autres l'ont évoqué, mais les expressions Rust produisent une valeur avec un type. Notamment, l'usage de return n'est pas nécessaire à la fin d'une fonction Rust, puisque la valeur de la dernière expression est retournée.
    Le typage est un mécanisme profond en Rust, et intervient également dans la sémantique de prêt. Le type ! existe depuis longtemps (en nightly) et contribue à la pleine cohérence du système de typage: il permet de typer un horizon indépassable: arrêt, panique, boucle infinie, ...
    C'est aussi le type minimal en Rust au sens où il peut être converti en n'importe quel type.

    À quoi cela peut servir concrètement? Je n'utilise pas le type ! personnellement, et ma réponse est forcément limitée. À certaines simplifications dans le langage, comme indiqué par Uther. Par ailleurs, il permet éventuellement plus de flexibilité dans la mise en œuvre des panic! / exit / todo! / ..., par exemple, y compris dans un contexte multithread [playground]:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #![feature(never_type)]
    use std::{ process::exit, thread::spawn };
     
    fn div<F: Fn() -> ! + Send + Sync>(left: f64, right: f64, failure: F) -> f64 {
        if right == 0.0 { failure(); } else { left / right }
    }
     
    fn panique1() -> ! { panic!("panique 1!"); }
    fn panique2() -> ! { panic!("panique 2!"); }
     
    fn main() {
        for h in [ 
            spawn(|| println!("f64 -> {}", div(32.0, 5.0, panique1))),
            spawn(|| println!("f64 -> {}", div(32.0, 0.0, panique2))),
            spawn(|| println!("f64 -> {}", div(128.0, 16.0, || todo!()))),
            spawn(|| println!("f64 -> {}", div(8.0, 0.0, || unreachable!())))
        ] { let _ = h.join(); }
        println!("f64 -> {}", div(12.0, 5.0, ||exit(0)));
    }

  8. #8
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    551
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 551
    Par défaut Merci à vous
    ,

    Merci pour vos réponses, il va me falloir un peu de temps pour les assimiler. Il me semble que ! a plusieurs "fonctionnaltés" qui n'ont pas forcément de relation entres-elles. J'ai repris les extraits qui m'ont "marqués" de vos réponses:

    imperio: Le ! est un type qui indique que le programme s'arrêtera dans cette fonction. Donc non, pas comme en C/C++ et consorts. C'est un confort en somme.
    Uther:Le point d’exclamation n'est pas pour le moment un type a part entière (il n'est utilisable qu'en retour de fonction).
    fdecode;il permet de typer un horizon indépassable: arrêt, panique, boucle infinie, ...
    fdecode:C'est aussi le type minimal en Rust au sens où il peut être converti en n'importe quel type.
    Pour imperio ! est un type "de confort" pour dire que le programme s'arrêtera dans la fonction. Uther ajoute que ce n'est pas pour le moment un type "a part entière" car il n'est utilisable qu'en retour de fonction. fdecode ajoute qu'il permet de typer un "horizon indépassable" (là j'ai du mal a saisir, que veut dire "typer un horizon (indépassable)", et que c'est le "type minimal" (donc, c'est un type ou pas ?), et qu'il peut être "converti en n'importe quel type.

    C'est pas mal d'usages pour un simple petit opérateur ! Je vous avoue que ce n'est pas facile a décrypter. C'est pas vraiment un type, mais c'est le type de base (un peu comme un "généric" ?) Utiliser un "type" pour indiquer qu'un programme s'arrêtera dans la fonction qui l'utilise, j'ai du mal a comprendre le "concept" caché derrière.

    Je vais potasser tout cela

    Encore merci à vous.
    BàV et Peace & Love.

    (PS: la définition de mon language Home se précise de plus en plus et j'avance bien, je n'ai pas abandonné, mais j'ai un peu "refactorisé" le code, et tant le langage que son compilateur se peaufine tout doucement. J'ai aussi commencer à attaquer la VM et le jeu d'instruction du CPU virtuel. Y'a encore du boulot, mais il me semble que j'avance sans trop de soucis, c'est juste beaucoup a faire...)

  9. #9
    Membre Expert Avatar de Uther
    Homme Profil pro
    Tourneur Fraiseur
    Inscrit en
    Avril 2002
    Messages
    4 748
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Tourneur Fraiseur

    Informations forums :
    Inscription : Avril 2002
    Messages : 4 748
    Par défaut
    En effet c'est un peu compliqué car la situation est en train de changer.
    Actuellement si on utilise le compilateur stable, ! n'est utilisable qu'en type de retour d'une fonction qui ne peut pas se terminer. On ne peut pas encore l'utiliser directement comme type(ou composant d'un type) d'une variable. Mais dans un avenir proche (ou actuellement si on active la fonctionnalité expérimentale sur la version nightly du compilateur), le type ! sera utilisable partout en tant que type.

    Comme dit imperio, ça sert en partie d'indication : on a l'assurance qu'une fonction qui retourne ! ne se terminera pas. Mais ça aussi de vraies incidences techniques comme j'ai essayé de le montrer dans mes exemples.
    Il faut se représenter le type ! comme un type non instanciable, qui sert a définir des cas impossibles. Dans mon second exemple, le type Result<u32,!> indique un type qui ne peux pas avoir de cas d'erreur. Si on essaie de lui affecter une erreur, il refusera de compiler et du coup on a plus a traiter ce cas.

  10. #10
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    551
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 551
    Par défaut Merci,
    Uther,

    Citation Envoyé par Uther Voir le message
    En effet c'est un peu compliqué car la situation est en train de changer.
    C'est en effet une des choses les plus difficiles avec Rust, ses changements et avancées petit bout par petit bout. Après 11 ans de développement, je pense qu'avec les moyens qu'ils ont, plus de "fonctionnalités" auraient déjà dû être "tranchées". Je pense que l'équipe derrière Rust devrait faire moins de "release" (tous les 2 ou 3 ans), et pas avoir une nouvelle version trop souvent avec une poignée de petits ajouts.

    Citation Envoyé par Uther Voir le message
    Actuellement si on utilise le compilateur stable, ! n'est utilisable qu'en type de retour d'une fonction qui ne peut pas se terminer. On ne peut pas encore l'utiliser directement comme type(ou composant d'un type) d'une variable. Mais dans un avenir proche (ou actuellement si on active la fonctionnalité expérimentale sur la version nightly du compilateur), le type ! sera utilisable partout en tant que type.
    Je comprend bien la nécessité d'un mode "expérimentale", mais ce n'est pas une raison pour sortir une nouvelle "spécification" du langage tous les 3 mois, je pense que l'équipe de Rust doit se poser la question, car vu de l'extérieur, on a l'impression que "ça part dans tous les sens". Un développeur "normal" (pas un 'insider'), devrait pouvoir se reposer sur une spec bien précise et qui a une durée de vie plus grande que la durée de sa lecture

    Citation Envoyé par Uther Voir le message
    Comme dit imperio, ça sert en partie d'indication : on a l'assurance qu'une fonction qui retourne ! ne se terminera pas. Mais ça aussi de vraies incidences techniques comme j'ai essayé de le montrer dans mes exemples.
    Et je t'en remercie, sois en certains.

    Citation Envoyé par Uther Voir le message
    Il faut se représenter le type ! comme un type non instanciable, qui sert a définir des cas impossibles. Dans mon second exemple, le type Result<u32,!> indique un type qui ne peux pas avoir de cas d'erreur. Si on essaie de lui affecter une erreur, il refusera de compiler et du coup on a plus a traiter ce cas.
    J'ai du mal avec la notion de "définir des cas impossibles", quel est l'intérêt d'une telle chose ?. Et si le type Result<u32!> refuse de compiler, je ne comprend pas sa nécessité, car il y a plus simple, pour un type donné, de détecter et refuser qu'on lui affecte une valeur qu'il ne peut pas accepter.

    Sans prétention ni sans vouloir me "comparer" avec l'équipe de Rust, dans mon langage Home, il n'y a au départ "aucun type" de base. Le compilateur génère cependant les cas les plus courant. Penons int8 par exemple. Avec le type int8, faire int8 a=300 sera refusé par le compilateur. Il est refusé car le compilateur sait que int8 ne peut contenir que des valeurs de 0~255. Si tu a int8 a, int16 b, faire un a := b sera refusé, car on tente d'affecter à une variable de type int8 une variable qui peut contenir une valeur > 255. Ces cas sont détectés à la compilation.

    Si tu as int8 a=255, et que tu fais a := a + 1, cela sera également refusé, car la partie Analyseur Static (en développement) détectera (toujours à la compilation) que l'opération donnera à int8 a une valeur "hors range". Pareil dans une boucle, où le nombre de boucle possible est déterminé, et que la possibilité d'affecter à int8 a une valeur >255 est "possible". Et le code sera refusé à la compilation.

    Aussi, lorsque l'utilisateur veut créer un type, disons intRoue, il (pourra) le faire via un def int Roue[1..4] et il sera impossible de faire intRoue nb=5 ni même nb := 0, car 5 et 0 sont "hors range" pour le type intRoue. Le compilateur et son copain l'Analyseur Static empêcherons ces cas à la compilation. (c'est du moins comme cela que je compte rendre le typage très "strict"). Je vais peut-être tomber sur des cas impossible a détecter, mais je ne vois pas lesquels pour le moment. C'est certains que l'analyseur static sera d'une importance capital pour que cela fonctionne à 100%.

    Je m'excuse d'être un rien "Hors Sujet", mais c'était pour énoncer des cas pour lequel le type ! de Rust joue un rôle.

    Ce ne sont que mes réflexions, et c'est en développement, donc je ne peux pas assurer que j'atteindrais mon but de la sorte, j'en suis bien conscient, j'expérimente...

    BàV et Peace & Love.

  11. #11
    Membre confirmé
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2021
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mai 2021
    Messages : 123
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    il permet de typer un horizon indépassable: arrêt, panique, boucle infinie, ..
    C'est aussi le type minimal en Rust au sens où il peut être converti en n'importe quel type.
    fdecode ajoute qu'il permet de typer un "horizon indépassable" (là j'ai du mal a saisir, que veut dire "typer un horizon (indépassable)", et que c'est le "type minimal" (donc, c'est un type ou pas ?), et qu'il peut être "converti en n'importe quel type.
    C'est à dire que votre programme ne pourra pas aller au delà du code qui "produit" une valeur typée !. Je mets des guillemets, car produire ici signifie ne pas aller au delà de ce code: c'est conceptuel mais cohérent par rapport aux mécanismes du langage.
    C'est minimal au sens de la hiérarchie de vos types, si on construit cette hiérarchie par le fait qu'un type puisse être converti en un autre.

    Votre question m'étonne. Vous savez ce que c'est qu'une relation d'ordre?

    > "type minimal" (donc, c'est un type ou pas ?)
    En quoi la minimalité altère la définition d'un objet?
    Si vous vous avez des parts de gâteau, et que vous prenez la plus petite part (la part de gâteau minimale), cela n'altère pas le fait que ce soit une part de gâteau...
    Vous avez en tête une autre conception de la minimalité?

    Citation Envoyé par OuftiBoy Voir le message
    C'est en effet une des choses les plus difficiles avec Rust, ses changements et avancées petit bout par petit bout.
    ! existe depuis longtemps: déjà présent avant la version 1. Son statut de type est officialisé avec Rust 1.41 (janvier 2020). On peut l'utiliser directement en nightly, mais avec des restrictions en stable. Les concepteurs du langage peuvent laisser en nightly certaines composantes et propriétés du langage qui sont utiles à la conception du langage sans les rendre accessibles en stable car elles peuvent présenter des difficultés pour une maîtrise par un utilisateur lambda (par exemple, ceux qui ne développent pas le langage). C'est sans doute le cas ici.

    Citation Envoyé par OuftiBoy Voir le message
    Après 11 ans de développement, je pense qu'avec les moyens qu'ils ont, plus de "fonctionnalités" auraient déjà dû être "tranchées".
    Pour ma part, je n'ai pas cette perception des "limitations de fonctionnalité" de Rust stable.

    Citation Envoyé par OuftiBoy Voir le message
    Je pense que l'équipe derrière Rust devrait faire moins de "release" (tous les 2 ou 3 ans), et pas avoir une nouvelle version trop souvent avec une poignée de petits ajouts.
    C'est ce qui correspond aux Éditions de Rust (tous les 3 ans).
    Pour le reste, c'est votre point de vue.


    Citation Envoyé par OuftiBoy Voir le message
    Je comprend bien la nécessité d'un mode "expérimentale", mais ce n'est pas une raison pour sortir une nouvelle "spécification" du langage tous les 3 mois,
    Le changement en profondeur (cela reste modéré) de Rust arrive avec les éditions, pas avec les updates.

    Citation Envoyé par OuftiBoy Voir le message
    je pense que l'équipe de Rust doit se poser la question, car vu de l'extérieur, on a l'impression que "ça part dans tous les sens". Un développeur "normal" (pas un 'insider'), devrait pouvoir se reposer sur une spec bien précise et qui a une durée de vie plus grande que la durée de sa lecture
    Ce n'est pas ma perception, et je suis un développeur normal, pas un insider.

    Citation Envoyé par OuftiBoy Voir le message
    J'ai du mal avec la notion de "définir des cas impossibles", quel est l'intérêt d'une telle chose ?. Et si le type Result<u32!> refuse de compiler, je ne comprend pas sa nécessité, car il y a plus simple, pour un type donné, de détecter et refuser qu'on lui affecte une valeur qu'il ne peut pas accepter.
    Vous n'êtes peut-être pas assez familier avec la notion de typage. Derrière tout ça, il y a aussi certaines considérations logiques. Dans une correspondance de Curry-Howard, le type ! peut correspondre au ⊥ logique.
    Cela pourrait avoir un intérêt pour ceux qui analysent logiquement le langage ; par exemple pour pour prouver des propriétés de sécurité du langage.
    Mais je m'avance peut-être un peu. Ce n'est pas mon domaine.

    Citation Envoyé par OuftiBoy Voir le message
    Et si le type Result<u32!> refuse de compiler, je ne comprend pas sa nécessité,
    Quel rapport avec le fait de refuser de compiler?

    Citation Envoyé par OuftiBoy Voir le message
    car il y a plus simple, pour un type donné, de détecter et refuser qu'on lui affecte une valeur qu'il ne peut pas accepter.
    De votre point de vue. Pour moi, le fait d'intégrer ces situations d'arrêt dans le typage rationalise l'ensemble du langage et permet d'éviter les traitements ad hoc. C'est donc à mes yeux plus simple.

  12. #12
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    551
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 551
    Par défaut Bonsoir
    fdecode,

    Citation Envoyé par fdecode Voir le message
    C'est à dire que votre programme ne pourra pas aller au delà du code qui "produit" une valeur typée !. Je mets des guillemets, car produire ici signifie ne pas aller au delà de ce code: c'est conceptuel mais cohérent par rapport aux mécanismes du langage. C'est minimal au sens de la hiérarchie de vos types, si on construit cette hiérarchie par le fait qu'un type puisse être converti en un autre.
    Je cromprend bien cela, je suis juste étonné qu'on utilise un opérateur pour cela.

    Citation Envoyé par fdecode Voir le message
    Votre question m'étonne. Vous savez ce que c'est qu'une relation d'ordre?
    > "type minimal" (donc, c'est un type ou pas ?)
    En quoi la minimalité altère la définition d'un objet?
    Si vous vous avez des parts de gâteau, et que vous prenez la plus petite part (la part de gâteau minimale), cela n'altère pas le fait que ce soit une part de gâteau... Vous avez en tête une autre conception de la minimalité?
    J'ai dis "c'est un type ou pas" car Uther disait que cela n'en était pas "tout à fait un". Et non, je n'ai pas une autre conception de la "minimalité". Je me suis sans doute mal exprimé.

    Citation Envoyé par fdecode Voir le message
    ! existe depuis longtemps: déjà présent avant la version 1. Son statut de type est officialisé avec Rust 1.41 (janvier 2020). On peut l'utiliser directement en nightly, mais avec des restrictions en stable. Les concepteurs du langage peuvent laisser en nightly certaines composantes et propriétés du langage qui sont utiles à la conception du langage sans les rendre accessibles en stable car elles peuvent présenter des difficultés pour une maîtrise par un utilisateur lambda (par exemple, ceux qui ne développent pas le langage). C'est sans doute le cas ici. Pour ma part, je n'ai pas cette perception des "limitations de fonctionnalité" de Rust stable.
    C'est bien normal, mais je ne parlais pas de "limitation de fonctionnalité", c'était plutôt dans le sens de "rétention" d'un concept durant 5 ans, en "nightly". Je trouve que c'est assez lent pour se décider si on l'autorise ou pas, c'est plutôt cela qui m'intrigue. Et je suis bien d'accord cependant que certains concepts ne sont pas facile a maîtriser, j'en suis la preuve vivante ;-)

    Citation Envoyé par fdecode Voir le message
    C'est ce qui correspond aux Éditions de Rust (tous les 3 ans). Pour le reste, c'est votre point de vue.
    Je plaide coupable, car je ne fait pas forcément attention, lorsqu'un article annonce une version, ou des fonctionnalités, si c'est en "insider", en "expérimentation", en "préversion", en "bêta"...

    Citation Envoyé par fdecode Voir le message
    Le changement en profondeur (cela reste modéré) de Rust arrive avec les éditions, pas avec les updates. Ce n'est pas ma perception, et je suis un développeur normal, pas un insider.
    Ok, ok, je comprend cela, mais Rust a un objectif bien précis (c'est du moins l'impression que j'en ai), c'est de "prendre le pas" sur le C++. Et je trouve étonnant que des changements "en profondeur" sont encore nécessaires après 11 ans de développement avec des moyens relativement important, alors qu'ils savaient "que qui n'allait pas" avec le C++ et savaient ce qu'ils voulaient faire.

    Mais chaque équipe mène son projet comme bon lui senble.

    Citation Envoyé par fdecode Voir le message
    Vous n'êtes peut-être pas assez familier avec la notion de typage. Derrière tout ça, il y a aussi certaines considérations logiques. Dans une correspondance de Curry-Howard, le type ! peut correspondre au ⊥ logique. Cela pourrait avoir un intérêt pour ceux qui analysent logiquement le langage ; par exemple pour pour prouver des propriétés de sécurité du langage. Mais je m'avance peut-être un peu. Ce n'est pas mon domaine.
    Oui, je vous rassure, je connais fort bien la notion de typage...

    Citation Envoyé par fdecode Voir le message
    Quel rapport avec le fait de refuser de compiler?
    Je répondais à Uther et de son exemple Result<u32,!>,

    En effet c'est un peu compliqué car la situation est en train de changer. Actuellement si on utilise le compilateur stable, ! n'est utilisable qu'en type de retour d'une fonction qui ne peut pas se terminer. On ne peut pas encore l'utiliser directement comme type(ou composant d'un type) d'une variable. Mais dans un avenir proche (ou actuellement si on active la fonctionnalité expérimentale sur la version nightly du compilateur), le type ! sera utilisable partout en tant que type. Comme dit imperio, ça sert en partie d'indication : on a l'assurance qu'une fonction qui retourne ! ne se terminera pas. Mais ça aussi de vraies incidences techniques comme j'ai essayé de le montrer dans mes exemples. Il faut se représenter le type ! comme un type non instanciable, qui sert a définir des cas impossibles. Dans mon second exemple, le type Result<u32,!> indique un type qui ne peux pas avoir de cas d'erreur. Si on essaie de lui affecter une erreur, il refusera de compiler et du coup on a plus a traiter ce cas.
    on a l'assurance qu'une fonction qui retourne ! ne se terminera pas. De mon point de vue, je ne vois pas comment une fonction peut "en-même tant" ne pas se terminer ET "en-même tant" retourner le type !


    De votre point de vue. Pour moi, le fait d'intégrer ces situations d'arrêt dans le typage rationalise l'ensemble du langage et permet d'éviter les traitements ad hoc. C'est donc à mes yeux plus simple.
    Oui, je pense qu'on n'a pas la même position face à cette "utilisation", moi je trouve cela "perturbant" et rend l'ensemble moins "simple".
    La preuve (selon moi), les différentes réponses que j'ai reçue (et je vous en remercie tous). Rust m'intrigue, j'essaye de le comprendre avec une certaine "hauteur" de vue, mais je ne développe pas avec, il a donc certainement des choses très simple que je comprend pas parce que je ne le pratique pas.

    Rust est une bonne initiative, il commence a percer, mais pour ma part, sauf pour essayer d'en apprendre certains "concepts", je n'y adhère pas, car plus je vois du code Rust, plus il me semble difficile a lire. C'est tout bête mais rien que la syntaxe (qui n'est qu'une affaire de "goût" et de "pratique"), me rebute tant elle me semble devenir de plus en plus ésotérique. Ce n'est pas un jugement, s'est mon ressenti, et chacun a le sien qui est tout a fait respectable.

    C'est peut-être un "biais" de ma part tant j'accorde une importance (démesurée peut-être) à la "lisibilité" d'un code source.

    Mais je vous remercie de vos précisions et de la bonne tenue de débattre poliment, argument et contre-argument. Même si on n'est pas d'accord.

    BàV et Peace & Love.

  13. #13
    Membre Expert Avatar de Uther
    Homme Profil pro
    Tourneur Fraiseur
    Inscrit en
    Avril 2002
    Messages
    4 748
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Tourneur Fraiseur

    Informations forums :
    Inscription : Avril 2002
    Messages : 4 748
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    C'est en effet une des choses les plus difficiles avec Rust, ses changements et avancées petit bout par petit bout. Après 11 ans de développement, je pense qu'avec les moyens qu'ils ont, plus de "fonctionnalités" auraient déjà dû être "tranchées". Je pense que l'équipe derrière Rust devrait faire moins de "release" (tous les 2 ou 3 ans), et pas avoir une nouvelle version trop souvent avec une poignée de petits ajouts.
    Rust n'évolue pas plus ni moins que la plupart des langages actifs comme Java, C++, C#, Go, ...
    Il faut voir que ajouter des fonctionnalités à un langage un minimum complexe, ça n'est pas anodin. Il faut s'assurer qu'il n' y a pas d'interactions négatives avec toutes les autres fonctionnalités, et qu'il n'y aura pas de mauvaises surprises à l'usage. C'est important de ne pas faire d'erreur car c'est difficile voire impossible dans certains cas de faire marche arrière une fois la fonctionnalité officialisée. Si les fonctionnalités restent un moment en mode expérimental, c'est justement pour être sur qu'elles ne posent pas de problèmes.

    Citation Envoyé par OuftiBoy Voir le message
    Je comprend bien la nécessité d'un mode "expérimentale", mais ce n'est pas une raison pour sortir une nouvelle "spécification" du langage tous les 3 mois, je pense que l'équipe de Rust doit se poser la question, car vu de l'extérieur, on a l'impression que "ça part dans tous les sens". Un développeur "normal" (pas un 'insider'), devrait pouvoir se reposer sur une spec bien précise et qui a une durée de vie plus grande que la durée de sa lecture
    Sauf que ton appréciation de la situation n'est pas du tout bonne, Rust ne change pas intégralement tous les quatre matins. En fait, rien n'a changé au sujet du type ! sur la version stable du compilateur depuis la sortie du langage il y a plus de 10 ans. La fonctionnalité est développé dans les version expérimentales de Rust dont l'utilisation n'est pas recommandé pour les utilisateurs finaux.
    Ce qui a été ajouté dans cette version stable de Rust, c'est juste des messages d'avertissement pour permettre de repérer et adapter des aujourd'hui des cas d'erreurs qui pourraient apparaitre quand le type `!` sera stabilisé.

    Citation Envoyé par OuftiBoy Voir le message
    J'ai du mal avec la notion de "définir des cas impossibles", quel est l'intérêt d'une telle chose ?. Et si le type Result<u32, !> refuse de compiler, je ne comprend pas sa nécessité, car il y a plus simple, pour un type donné, de détecter et refuser qu'on lui affecte une valeur qu'il ne peut pas accepter.
    Une variable de type Result<Valeur, Erreur> contient soit une valeur de type Valeur ou une erreur de type Erreur. C'est le moyen le plus courrant en Rust de retourner la valeur d'une opération qui peut échouer.
    Donc la variable de type Result<u32, !> de mon exemple est tout a fait utilisable, c'est juste qu'elle ne pourra pas contenir de cas d'erreur, seulement une valeur valide de type u32.

    Citation Envoyé par OuftiBoy Voir le message
    Sans prétention ni sans vouloir me "comparer" avec l'équipe de Rust, ...
    En effet la vérification du domaine de validité d'une variable est une fonctionnalité intéressante. Il y a 'ailleurs des gens qui expérimentent comment apporter ça dans Rust, mais là, on est pas du tout dans le même sujet.

    Citation Envoyé par OuftiBoy Voir le message
    Je cromprend bien cela, je suis juste étonné qu'on utilise un opérateur pour cela.
    Il ne s'agit pas d'un opérateur, mais juste d'un type spécial qui permet certaine largesses.

    Citation Envoyé par OuftiBoy Voir le message
    C'est bien normal, mais je ne parlais pas de "limitation de fonctionnalité", c'était plutôt dans le sens de "rétention" d'un concept durant 5 ans, en "nightly". Je trouve que c'est assez lent pour se décider si on l'autorise ou pas, c'est plutôt cela qui m'intrigue. Et je suis bien d'accord cependant que certains concepts ne sont pas facile a maîtriser, j'en suis la preuve vivante ;-)
    5 ans, pour un langage comme le Rust c'est pas forcément très long. Il faut voir que un langage comme Rust, relativement complexe, et qui tient à assurer le plus possible la compatibilité ascendante, il faut être prudent et composer avec l'existant.

    Citation Envoyé par OuftiBoy Voir le message
    Ok, ok, je comprend cela, mais Rust a un objectif bien précis (c'est du moins l'impression que j'en ai), c'est de "prendre le pas" sur le C++. Et je trouve étonnant que des changements "en profondeur" sont encore nécessaires après 11 ans de développement avec des moyens relativement important, alors qu'ils savaient "que qui n'allait pas" avec le C++ et savaient ce qu'ils voulaient faire.
    Il a été prévu des le début que le type never serait généralisé plus tard, mais l'équipe de Rust n'est quand même pas illimitée, il y a de nombreux points sur lesquels elle travaille. Le type never n'est clairement pas une urgence.

    Citation Envoyé par OuftiBoy Voir le message
    on a l'assurance qu'une fonction qui retourne ! ne se terminera pas. De mon point de vue, je ne vois pas comment une fonction peut "en-même tant" ne pas se terminer ET "en-même tant" retourner le type !
    Ce n'est pas parce que la fonction ne se terminera pas au moment de l’exécution qu'elle n'a pas un type de retour défini dans le code.

  14. #14
    Membre confirmé
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Mai 2021
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Mai 2021
    Messages : 123
    Par défaut
    Citation Envoyé par Uther Voir le message
    Citation Envoyé par OuftiBoy
    on a l'assurance qu'une fonction qui retourne ! ne se terminera pas. De mon point de vue, je ne vois pas comment une fonction peut "en-même tant" ne pas se terminer ET "en-même tant" retourner le type !
    Ce n'est pas parce que la fonction ne se terminera pas au moment de l’exécution qu'elle n'a pas un type de retour défini dans le code.
    Une fonction qui ne se termine pas peut-elle retourner un type? Bien entendu, j'acquiesce à cela, mais il y a un saut conceptuel qui n'est pas évident pour tout le monde.

    Toute proportion gardée, il y a d'autres cas ressemblants.
    • Pendant longtemps, le 0 n'existait pas parce qu'il était difficile de concevoir qu'un nombre puisse ne rien dénombrer.
    • La contradiction logique a posé quelques problèmes à certains mathématiciens (cf. début XX-ième), car le fait de raisonner avec la 'contradiction' paraissait inadéquat. Cela a donné par exemple la logique intuitionniste qui retire le tiers exclus des modes de raisonnement acceptés.

    Donc, en effet, on peut comprendre que formaliser un type associé à une fonction qui ne retourne rien puisse paraitre inutilement contre intuitif. Mais ce genre de généralisation du formalisme s'est toujours avéré fructueux (exemple du 0 et du ).

  15. #15
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    551
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 551
    Par défaut Bonjour Uther


    Citation Envoyé par Uther Voir le message
    Rust n'évolue pas plus ni moins que la plupart des langages actifs comme Java, C++, C#, Go, ...
    Il faut voir que ajouter des fonctionnalités à un langage un minimum complexe, ça n'est pas anodin. Il faut s'assurer qu'il n' y a pas d'interactions négatives avec toutes les autres fonctionnalités, et qu'il n'y aura pas de mauvaises surprises à l'usage. C'est important de ne pas faire d'erreur car c'est difficile voire impossible dans certains cas de faire marche arrière une fois la fonctionnalité officialisée. Si les fonctionnalités restent un moment en mode expérimental, c'est justement pour être sur qu'elles ne posent pas de problèmes.
    Je suis tout à fait d'accord avec toi, il ne faut pas "casser" l'existant. Le mode "expérimental" est là pour ça. Je m'étonnais juste que cela prenne autant de temp. Mais le temps, c'est relatif ;-)

    Citation Envoyé par Uther Voir le message
    Sauf que ton appréciation de la situation n'est pas du tout bonne, Rust ne change pas intégralement tous les quatre matins. En fait, rien n'a changé au sujet du type ! sur la version stable du compilateur depuis la sortie du langage il y a plus de 10 ans. La fonctionnalité est développé dans les version expérimentales de Rust dont l'utilisation n'est pas recommandé pour les utilisateurs finaux.
    Ce qui a été ajouté dans cette version stable de Rust, c'est juste des messages d'avertissement pour permettre de repérer et adapter des aujourd'hui des cas d'erreurs qui pourraient apparaitre quand le type `!` sera stabilisé.
    Si c'est en "expérimentation" depuis 10 ans (10 ans, moi je trouve ça long ;-)) et que ce n'est toujours pas disponible dans une version "stable", ce serait pour moi un signal que l'idée n'est pas forcément bonne. Mais c'est ma perception des choses.

    Citation Envoyé par Uther Voir le message
    Une variable de type Result<Valeur, Erreur> contient soit une valeur de type Valeur ou une erreur de type Erreur. C'est le moyen le plus courrant en Rust de retourner la valeur d'une opération qui peut échouer.
    Donc la variable de type Result<u32, !> de mon exemple est tout a fait utilisable, c'est juste qu'elle ne pourra pas contenir de cas d'erreur, seulement une valeur valide de type u32.
    C'est une bonne chose, et cela devrait même être le cas "par défaut". Par contre, je n'aime pas trop l'idée que la "Valeur" d'un type représente une "Valeur" ou "Une erreur de type". Se "mélange" de 2 concepts en 1, je n'adhère pas. Un type ne devrait contenir que des valeurs bien définies (celles "compatibles" avec son type). L'erreur (qui ne devrait pas arriver si on sait "valider" qu'un type ne contient que des valeurs cohérentes avec son "type") devrait être signalée autrement, et pas être "mélangée" ainsi. Pour une simple assignation, c'est assez facile de valider cette dernière. Pour les incrémentation et/ou ajout, un analyseur "static" devrait pouvoir détecter l'erreur. Mais je ne comprend peut-être pas tout et je fais peut-être une erreur, mais mélanger 2 concepts en 1, je trouve cela étrange.

    Citation Envoyé par Uther Voir le message
    En effet la vérification du domaine de validité d'une variable est une fonctionnalité intéressante. Il y a 'ailleurs des gens qui expérimentent comment apporter ça dans Rust, mais là, on est pas du tout dans le même sujet.
    On est un peut dans le même sujet quand même selon moi. Pour moi, la "validité d'une variable" n'est pas une fonctionnalité intéressante, c'est une fonctionnalité "indispensable". Si un "type" peut retourner une "erreur", il faut scinder ce concept. Je comprend que cela soit difficile si c'est depuis le début de Rust, de trouver une solution à cela sans "casser" l'existant. Si on assigne (directement et/ou pas incrémentation) ou qu'on fait se produire un "Type Overflow ou Type Underflow", il faut le détecter, et signaler l'erreur et refuser la compilation. Il faut tout faire pour éviter que cela puisse être possible de se produire. Mais n'étant pas assez au courant du développement de Rust, je fais peut-être fausse route.

    Citation Envoyé par Uther Voir le message
    Il ne s'agit pas d'un opérateur, mais juste d'un type spécial qui permet certaine largesses.
    Le fait que ce "type spécial" soit représenter par ce qui "ressemble" à un "opérateur", soit, c'est un choix de syntaxe (qu'on aime ou pas, ce n'est pas le sujet), le sujet c'est qu'un type permette "certaines largesses". C'est (selon moi, je ne dis pas pas qu'il y a de bon arguments pour agir ainsi) une erreur de "Design" du langage. Un type est un type, et rien d'autre. L'introduction d'un type "spécial" n'est pas (toujours selon mon avis) une bonne chose. C'est ouvrir la porte à nombre de soucis qui n'existeraient tout simplement pas si un type était un type et rien d'autre.

    Citation Envoyé par Uther Voir le message
    5 ans, pour un langage comme le Rust c'est pas forcément très long. Il faut voir que un langage comme Rust, relativement complexe, et qui tient à assurer le plus possible la compatibilité ascendante, il faut être prudent et composer avec l'existant.
    Comme dit plus haut, le temps c'est relatif, un "jeune" peut trouver ça "court" (il a toute la vie devant lui), mais un "vieux" sachant qu'il n'est pas éternel peut trouver cela "long", non ?

    Citation Envoyé par Uther Voir le message
    Il a été prévu des le début que le type never serait généralisé plus tard, mais l'équipe de Rust n'est quand même pas illimitée, il y a de nombreux points sur lesquels elle travaille. Le type never n'est clairement pas une urgence.
    Je comprend cela.

    Citation Envoyé par Uther Voir le message
    Ce n'est pas parce que la fonction ne se terminera pas au moment de l’exécution qu'elle n'a pas un type de retour défini dans le code.
    Ok, je n'y vois aucun intérêt, mais il y a certainement une "bonne justification" pour agir ainsi, mais j'avoue qu'elle m'échappe. Je ne suis pas "la tête dans le guidon", donc je ne vois le développement de Rust que d'un point de vue externe.

    PS: (HS?) J'essaye de gérer les types d'une autres manière dans mon petit langage, donc je "suis" dans la même "problématique". En interne, un type est un "range" (de 0 à 255) pour un int8 par exemple. Impossible de lui "assigner" une valeur "Hors Type" (enfin si, mais c'est détecter par le compilateur, qui émet une erreur). Ce que je dois encore ajouter, c'est que lorsqu'on a affecté une valeur à un variable, c'est de "réduire" ce qu'on peut encore y ajouter. Lorsque l'on a fait un int8 a := 254, on ne peut plus faire qu'un "add 1", "add 2" sera refusé (à la compilation). En gros, le compilateur "maintient" ce qu'il reste "possible" d'ajouter à un une variable d'un certains type. Si on ajouter à un type int8 une valeur d'un type int4, on retire "16" (le max d'un type int4) à ce que l'on peut encore ajouter au type int8. Avec ces principes de base, et un analyseur static pour "suivre" les changements d'un type dans les boucle (par exemple), on peut (je pense) avoir des types "très strict". Tout ceci se fait à la compilation, pas en "runtime".

    Est-ce "restrictif" ? On peut le penser au premier abord, mais ça ne l'est pas. C'est juste "forcer" le développeur a bien définir les types dont il a besoin. Si on parle des "roues" d'une voiture, le nombre de roues est de type "int2" (0..3, soit 4 roues). Utiliser un int8 en essayant de valider qu'il ne dépassera pas 3, c'est peut-être possible, mais en utilisant un int2, c'est déjà plus simple dans grand nombre de cas.

    Je pense qu'on parle de type par "habitude" (int8, int16, int32), alors que (je pense), il faut penser en terme de "range": def int intRoue 0..3. Tout comme en POO, on "pense" en terme d'objet, alors (toujours selon-moi) on doit penser en terme de "comportement" (d'interface en quelque sorte).

    @Tous, je ne prétend nullement avoir raison, je pense juste (après une assez longue carrière), d'essayer d'aborder nombre de sujets d'un "autre point de vue", en partant d'une feuille blanche. Pour le moment, ça tient la route, et je verrai bien si je dérape. Disons que je suis en mode 'insider' :-)

    Merci à tous et si vous voyez une erreur dans mon approche, critiquer la svp, cela me rendra un grand service. Je ne prend jamais ombrage et sais reconnaître quand j'ai tord, surtout dans un débat animé, mais argumenter et courtois.

    BàV et Peace & Love.

  16. #16
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    551
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 551
    Par défaut Bonjour
    fdecode,

    Citation Envoyé par fdecode Voir le message
    Une fonction qui ne se termine pas peut-elle retourner un type? Bien entendu, j'acquiesce à cela, mais il y a un saut conceptuel qui n'est pas évident pour tout le monde.
    Oui, j'avoue que ce n'est pas facile pour moi.

    Citation Envoyé par fdecode Voir le message
    Toute proportion gardée, il y a d'autres cas ressemblants.
    • Pendant longtemps, le 0 n'existait pas parce qu'il était difficile de concevoir qu'un nombre puisse ne rien dénombrer.
    • La contradiction logique a posé quelques problèmes à certains mathématiciens (cf. début XX-ième), car le fait de raisonner avec la 'contradiction' comme s'il s'agissait d'une proposition logique 'normale' paraissait inadéquat. Cela a donné par exemple la logique intuitionniste qui retire le tiers exclus des modes de raisonnement acceptés.
    Je ne suis pas un grand mathématicien, loin de là. Je n'ai jamais entendu parler de la contradiction logique de . Ni des quelques exemple dont tu as parler avant dans un post précédent. Et j'avoue mon ignorance lorsque tu dis:

    la logique intuitionniste qui retire le tiers exclus des modes de raisonnement acceptés

    Je n'y comprend rien, zéro ;-)

    Par contre, je ne comprend pas pourquoi le "0" a posé problème si longtemps, c'est même pour moins inconcevable que cela ai été un problème. Le 0 est même pour moi la plus intuitive des valeurs.

    Si je suis un cromagnon, je regarde dans mon assiette, et ne saurait pas "compter" combien de "patate" il reste dedans, mais par contre, s'il n'y a plus rien dans l'assiette, je n'ai (sans avoir besoin de savoir compter) pas difficile de voir qu'il ne reste rien. C'est le 0.

    Citation Envoyé par fdecode Voir le message
    Donc, en effet, on peut comprendre que formaliser un type associé à une fonction qui ne retourne rien puisse paraitre inutilement contre intuitif. Mais ce genre de généralisation du formalisme s'est toujours avéré fructueux (exemple du 0 et du ).
    Je te crois, mais je n'ai pas la compétence nécessaire pour comprendre. Donc oui, c'est contre-intuitif, et je pense que ça l'est pour la majorité des "non-matheux". En introduisant dans un "langage informatique" des concepts "mathématiques" que la majorité des mortelles ne comprend pas, rend ce dernier assez "obscur" (pour moi, c'est le cas). Je ne juge pas, c'est mon ressenti. J'ai un biais pour éliminer tout ce qui n'est pas "simple" ou difficilement compréhensible. Et ceci parce que je pense qu'on peut "construire" des choses "compliquées" en utilisant uniquement des choses "simples". C'est la combinaisons de choses "simples" qui rend "possible" la création de choses "compliquées".

    Cette discussion est pour moi passionnante...

    BàV et Peace & Love.

  17. #17
    Membre Expert Avatar de Uther
    Homme Profil pro
    Tourneur Fraiseur
    Inscrit en
    Avril 2002
    Messages
    4 748
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Tourneur Fraiseur

    Informations forums :
    Inscription : Avril 2002
    Messages : 4 748
    Par défaut
    Citation Envoyé par OuftiBoy Voir le message
    Si c'est en "expérimentation" depuis 10 ans (10 ans, moi je trouve ça long ;-)) et que ce n'est toujours pas disponible dans une version "stable", ce serait pour moi un signal que l'idée n'est pas forcément bonne. Mais c'est ma perception des choses.
    C'est plus une question de priorité, et de technique. De loin, on ne se rend pas forcément compte de toutes les interdépendances, dans les langages qui ont un minimum de complexité. Tout ce qui touche au typage est complexe, particulièrement en Rust où c'est quelque chose de très important..

    Citation Envoyé par OuftiBoy Voir le message
    C'est une bonne chose, et cela devrait même être le cas "par défaut". Par contre, je n'aime pas trop l'idée que la "Valeur" d'un type représente une "Valeur" ou "Une erreur de type".
    C'est juste un exemple pour t'expliquer l’intérêt du type never en Rust. On va pas refaire tout le langage sur chaque point où il ne fonctionne pas comme tu voudrais que ton langage fonctionne, sinon on s'en sortira pas.
    Le type Result est une énumération avec deux variants possibles: Ok et Err. Dans mon exemple la seconde valeur est invalidée par le fait quelle est de type !. Result n'est qu'un exemple, ! pourra s'appliquer à toute énumération Rust dont le type des variants est paramétrisée.

    Citation Envoyé par OuftiBoy Voir le message
    PS: (HS?) J'essaye de gérer les types d'une autres manière dans mon petit langage, donc je "suis" dans la même "problématique"...
    Ca fait parti des chose qui sont dans les cartons en Rust, c'est totalement possible en théorie, jusqu'à un certain point car il y a des choses qui vont dépendre du runtime. Mais il faut voir que si tu vas sur ce chemin, là tu vas te rendre compte que sa pose plein de problématiques, c'est pas pour rien que Rust a remis ça a plus tard.

  18. #18
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    551
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 551
    Par défaut Bonsoir,
    Uther, merci pour cette belle discution,

    Citation Envoyé par Uther Voir le message
    C'est plus une question de priorité, et de technique. De loin, on ne se rend pas forcément compte de toutes les interdépendances, dans les langages qui ont un minimum de complexité. Tout ce qui touche au typage est complexe, particulièrement en Rust où c'est quelque chose de très important...
    C'est bien normal, le respect des types est fondamentale. Et ça devrait l'être dans tout langage.

    Citation Envoyé par Uther Voir le message
    C'est juste un exemple pour t'expliquer l’intérêt du type never en Rust. On va pas refaire tout le langage sur chaque point où il ne fonctionne pas comme tu voudrais que ton langage fonctionne, sinon on s'en sortira pas.
    Oui, je comprend bien cela, ça n'aurait aucun sens en plus. C'est pourquoi les "premières décisions" sont les plus importantes, car elles orientent le développement du langage.

    Citation Envoyé par Uther Voir le message
    Le type Result est une énumération avec deux variants possibles: Ok et Err. Dans mon exemple la seconde valeur est invalidée par le fait quelle est de type !. Result n'est qu'un exemple, ! pourra s'appliquer à toute énumération Rust dont le type des variants est paramétrisée.
    Là, je comprend mieux, lorsque tu dis Le type Result est une énumération avec deux variants possibles: Ok et Err, je comprend mieux l'ensemble de la discution. C'est un petit "tilt" qui c'est produit dans ma petite tête. Il faut peut de chose pour mieux se comprendre parfois...

    Citation Envoyé par Uther Voir le message
    Ca fait parti des chose qui sont dans les cartons en Rust, c'est totalement possible en théorie, jusqu'à un certain point car il y a des choses qui vont dépendre du runtime. Mais il faut voir que si tu vas sur ce chemin, là tu vas te rendre compte que sa pose plein de problématiques, c'est pas pour rien que Rust a remis ça a plus tard.
    C'est pourtant le chemin que je prend, c'est d'ailleurs en partie déjà implémenté (j'ai commencer par ça ce matin), et ce n'est pas si compliqué que cela, du moins pour l'instant je ne vois pas de "mur" devant moi, mais bon, ce ne serait pas la première fois que je me trompe. C'est un peut HS mais c'est également très proche du sujet, voici quelques exemples:

    Par exemple, le petit programme suivant refusera de compiler:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    program demo:
     
        int8 over=255
        over := over + 1
     
    end
    Le compilateur détecte l'overflow et génère le message suivant:

    xdemo.home 5: 19 'over' Can't be Assigned with '256', a value > 'int8 over' (max 255). [TPX/4] - Type Overflow Error.

    Dans ce cas, je peux être précis dans le message d'erreur, car le compilateur connait la valeur du literal '1'. Si le literal '1' est remplacé par une variable, je ne pourrais me baser pour le message d'erreur que sur la valeur maxi du type de la variable que l'on voudrait ajouter à 'over'.

    Si je change l'initialisation de 'over' de 255 à 252, et que je lui ajoute une variable de type 'int2' (dont le compilateur ne peut connaitre la valeur exact, mais bien la valeur maxi de son type, '3'), il n'y aura pas d'erreur, car 252 + max(3) peut-être contenue dans le type 'int8'. C'est également possible d'être précis parce que l'initialisation de 'over' se fait avec un 'literal' :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    program demo:
     
        int8 over=252
        int2 test=3
        over := over + test
     
    end
    Le code compile parfaitement.

    Mais avec le programme suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    program demo:
     
    	int8 over=252
    	int4 test=3
            over := over + test
     
    end
    Le compilateur refusera, car il ne peut pas connaitre la 'valeur' qu'aura 'test' lors de l'exécution. Il se basera sur la valeur maxi du type int4 (qui pourrait être de 15), et 252 + 15 ne peut être assigné à un int8.

    Le compilateur maintient pour chaque variable, la valeur maxi, mais aussi la valeur (maxi) restante (ou le plus grand type) qu'on peut lui ajouter. C'est possible car derrière le 'type' se cache derrière la notion de "range". Le compilateur est donc très strict, et je pense que c'est une bonne chose. Dès qu'on "modifie" une variable, le compilateur mets à jour le maximum qu'on peut encore lui ajouter, en se basant sur la valeur maxi d'un type qu'on lui ajoute ou retire. Il sait ainsi s'il y'a un "risque" d'overflow. C'est peut-être un peu extrême, mais cela évite un grand nombre d'erreur. Un risque d'overflow ou d'underflow n'est pas autorisé, sera détecté et ne compilera pas.

    faire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    program demo:
     
    	int8 a, b, c
    	c := a + b
     
    end
    N'as pas de sens, et ne compilera pas, car ajouter 2 int8 à 1 int8, pourrait provoquer un type overflow, et c'est plus pratique que le compilateur le détecte, que de devoir s'assurer (par du code en plus), s'il n'y a pas d'overflow en testant des limites un peu partout.

    Certes, il faut que le développeur "connaisse" les maxi qu'il veut pouvoir atteindre, et créer des types en conséquence. C'est un peu plus de réflexion avant, mais qui met définitivement de côté pleins de soucis. Par exemple, en 'C' on utilise un int8 pour une boucle mais si on ne doit pas dépasser le buffer, il faut gérer la "borne maxi" à la main (il doit donc de toute manière connaître son maxi). Ici, le compilateur s'en charge, sans code en plus que ce qu'il ne faudrait faire à chaque fois qu'on utilise un int8 mais qui ne doit pas dépasser 16.

    C'est faisable et même assez facilement, car dès le départ, j'ai pensé "range" et pas "type", donc depuis le début un type a un range bien précis, et l'utilisateur peut définir ceux qui ne sont pas là de base (comme int8).

    Qu'en pensez-vous ? Suis-je en train de me planter ? Voyez-vous un problème que je ne vois pas ? Est-ce trop extrême ?

    Merci d'avance.

    BàV et Peace & Love.

  19. #19
    Membre Expert Avatar de Uther
    Homme Profil pro
    Tourneur Fraiseur
    Inscrit en
    Avril 2002
    Messages
    4 748
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Tourneur Fraiseur

    Informations forums :
    Inscription : Avril 2002
    Messages : 4 748
    Par défaut
    Je pense que c'est une idée très intéressante, a laquelle j'ai réfléchi moi même (https://internals.rust-lang.org/t/fo...217/38?u=uther), mais qui nécessite encore beaucoup d'évolution sur le système de typage pour être implémentable en Rust. Sur un langage plus simple et ou c'est prévu dès le début, elle est probablement plus facilement réalisable.

    Une limitation que je vois dans ton approche c'est que je ne vois pas de distinction claire entre le type final et l’intervalle de validité. L'ensemble de données valides d'une variable n'est pas forcément une puissance de deux et ne commence pas forcément à 0 ou -2^n-1.

    De plus il te faudra un mécanisme d'échappement, avec vérification des limites au runtime si tu veux toujours garantir la cohérence, car il y a des cas utiles que tu ne pourras pas prouver à la compilation comme l'incrémentation d'une variable dans une boucle.

  20. #20
    Membre confirmé
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Mai 2015
    Messages
    551
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Mai 2015
    Messages : 551
    Par défaut Bonjour Uther,


    Citation Envoyé par Uther Voir le message
    Je pense que c'est une idée très intéressante, a laquelle j'ai réfléchi moi même (https://internals.rust-lang.org/t/fo...217/38?u=uther), mais qui nécessite encore beaucoup d'évolution sur le système de typage pour être implémentable en Rust. Sur un langage plus simple et ou c'est prévu dès le début, elle est probablement plus facilement réalisable.
    Oui, j'avais ça en tête dès le début. Je suis parti d'une feuille blanche, et j'ai dès le début implémenté les types (d'entier et ou de fpn), comme étant un intervalle de valeurs, avec une valeur mini et une autre maxi. La première "utilisation" de cela a justement été les boucles.

    Par exemple, déclarer un tableau en 2D et l'utiliser, cela se fait avec un code comme ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
        int8 screen[x=4, y=5]
        r10 := screen[x=0, y=2]
     
        for item in screen[x, y]:
        next
     
        screen[x=2, y=2] := 13
    int8 screen[x=4, y=5] déclare un tableau d'int8, nommé screen et qui a 2 dimensions x, et y. En interne, x est "définit" non pas par un type, mais par un range automatiquement créé par le compilateur et ayant un mininum de 0 et comme maximum la valeur qu'on lui donne à l'initialisation. Idem pour y.

    On ne peut accéder au tableau qu'en utilisant ces "range" x et y, on ne peut pas faire r10 := screen[0,4], il faut utiliser les ranges. Comme ceux-ci sont "bornés" avec leur valeur mini et maxi, il est impossible d'accéder à une valeur "hors tableau".

    Pour la boucle, item est automatiquement définit comme étant du type de ce que contient le screen, donc int8 (mais on pourrait donner le nom adapté que l'on veut au lieu d'item). Et il faut utiliser également les "accesseurs" (qui sont des ranges) x, y. On n'a pas de boucle imbriquée, et il est impossible de lire/écrire hors du tableau. Plus tard, j'ajouterai des "slices" et la possibilité de faire in screen[x=2, y=1..3], pour ne parcourir qu'un sous ensemble du tableau.

    Citation Envoyé par Uther Voir le message
    Une limitation que je vois dans ton approche c'est que je ne vois pas de distinction claire entre le type final et l’intervalle de validité. L'ensemble de données valides d'une variable n'est pas forcément une puissance de deux et ne commence pas forcément à 0 ou -2^n-1.
    C'est justement l'intérêt de penser en terme de "range" et non de "type". On peut définir def nbNecessaire 3..9. Pour l'affectation, on ne peut assigner à un type/range qu'un type/range qui en est un sous ensemble.

    En interne, l'intervalle pour un int8 est de mini=0, maxi=255. Un int2 a un mini de 0 et un maxi de 3. Il n'y a pas en interne de type int2, mais un "range" allant de mini à maxi. Un utilisateur pourra par exemple créer un type comme ceci: def nbRoue 1..4 pour désigner la roue d'une voiture. nbRoue ne pourra jamais avoir une valeur à 0 ou une valeur plus grande que 4.

    Lorsque l'on passe au code compilé, on utilise le un "vrai type", le plus petit possible pouvant contenir le range, mais à ce moment on a déjà écarté tout risque de débordement, et je pense même que le code assembleur finalement généré est plus "optimale/petit" qu'en C, car il ne faut plus rien "faire tester" via le code code assembleur. La seul chose que le code assembleur doit faire, c'est de tester pour une boucle si elle est terminée (via un simple compteur décrémenté à chaque tour de boucle) en testant (BRnz) si elle est terminée. Pas besoin de plus. En tout cas, pour le moment, j'arrive assez facilement à générer un code assembleur où je ne voit rien de superflu, car ce "superflu" est déjà écarté par le compilateur.

    Citation Envoyé par Uther Voir le message
    De plus il te faudra un mécanisme d'échappement, avec vérification des limites au runtime si tu veux toujours garantir la cohérence, car il y a des cas utiles que tu ne pourras pas prouver à la compilation comme l'incrémentation d'une variable dans une boucle.
    Je pense que je n'aurait pas besoin de faire quoi que ce soit au runtime, car les boucles sont "bornées", et les types/range aussi. On a donc à la compilation une vérification de la valeur mini et maxi dès la compilation. Lors d'une affectation à l'intérieur d'une boucle (et même de boucles imbriquées), le compilateur connait la borne maxi de chaque boucle, et donc le nombre maxi de fois qu'on peut affecter une certaine valeur (qui est un range ayant un maxi) d'une variable présent dans une boucle. Cela a l'aire "extrême", mais cela ne l'est pas du tout, il faut juste employer les bons types/ranges. L'équivalent en 'C', ce serait d'ajouter à chaque boucle imbriquée une limite pour ne pas avoir de débordement. Ici, tout cela est fait à la compilation, sans erreur possible au moment du "runtime" (car tout a été vérifié avant par le compilateur.

    Un dernier petit exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
        int4 test=0 ; test peut aller de 0 à 15
        test := test + 5 ; Ok, il reste une valeur "available" de 10 qu'on peut ajouter à test
        test := test + 9; Ok, il reste une valeur "available" de 1 qu'on peut ajouter à test
        test := test + 2; KO Type overflow, 14 + 2 = 16 qui est > que le maxi d'un int4.
    Si on remplace les 'literal' par des variables, on utilise le maxi du range d'une variable pour avoir le pire des scénario lors de l'affectation. Et comme on peut définir très précisément un type/range, cela ne pose pas de problème. Dans une boucle, on a le maxi via les "accesseurs" (qui sont des ranges avec un maxi), et la valeur maxi du "type/range" de la variable qu'on affecte.

    Je trouve la syntaxe clair, légère (pas besoin de boucles imbriquées, pas besoin de tester des limites, pas besoin de devoir "limiter" à 16 un int8 classique avec un if var < 16, le compilateur, grâce à la notion de "range" peut faire toute les vérification des cas "limite/maximum" et générer une erreur si tel est le cas lors d'une affectation.

    J'omets peut-être un cas, mais actuellement, je ne l'ai pas rencontré, et "mathématiquement", avec la valeur "maxi" et la "valeur restante", je pense qu'on peut prouver que les cas "limites" ne seront jamais dépassés. Peut-être que "fdecode" (qui est bien fort que moi en math) pourrait nous donner son avis et/ou la preuve que tout ceci tient la route.

    Une dernière chose, il n'y a pas de "casting" comme en C. Un type/range a un mini et un maxi immuable.

    Merci de ton temps pour suivre cela et me faire tes réflexions, n'hésite jamais à me dire que je me plante quelque part, si c'est argumenté et poli, je prend ça comme un "challenge" soit de répondre, soit de trouver ou d'affiner une solution ou de carrément d'abandonner une idée si elle est "invalidée" par quelque chose que je n'aurait pas vu.

    Encore un grand merci de me lire.

    BàT et Peace & Love.

Discussions similaires

  1. [Oracle 9i] Dé activer les contrainte d'une table
    Par shaun_the_sheep dans le forum Oracle
    Réponses: 2
    Dernier message: 30/11/2006, 09h59
  2. [Postgres 8] Activer les index
    Par julienOriano dans le forum PostgreSQL
    Réponses: 7
    Dernier message: 28/10/2005, 00h59
  3. [EasyPHP] Activer les fonctions LDAP sur EasyPHP 1.8
    Par gregfly26 dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 5
    Dernier message: 24/10/2005, 17h13
  4. Activer les suppression en cascade pour toutes contraintes
    Par jdeboer dans le forum MS SQL Server
    Réponses: 1
    Dernier message: 07/10/2005, 11h50
  5. [eclipse3.0.1] activer les assertions
    Par MicroPuce dans le forum Eclipse Java
    Réponses: 4
    Dernier message: 15/04/2005, 16h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo