Encore une couche de rouille avec Rust 0.11

53
14
juil.
2014
Technologie

Rust 0.11 est sorti le 2 juillet 2014 ! Pour rappel, Rust est un langage de programmation système qui vise la performance et la sûreté. Il est développé par Mozilla, en parallèle d'un nouveau moteur de rendu expérimental écrit en Rust, Servo.

Rust est open source. Son développement est fait de façon ouverte sur GitHub et le code source est publié sous double licence Apache 2.0 et licence MIT.

Rust

Sommaire

Rapide présentation de Rust

Objectifs du langage

L’objectif de Rust est d’être un langage pour l’écriture de programmes systèmes, habituellement écrits en C++. Tout comme C++, Rust permet un contrôle explicite de la gestion de la mémoire et d’autres fonctionnalités du système d’exploitation et du matériel, pour écrire des programmes les plus efficaces possibles.

Rust diffère cependant de C++ en mettant l’accent sur la sûreté : grâce à un système de types fort et à l’intégration dans ce système de types de nombre d’idiomes de gestion de la mémoire (on y retrouve les sémantiques d’ownership, de move, mais également des concepts plus innovants comme l’analyse des lifetimes), le compilateur est capable de détecter à la compilation un très grand nombre d’erreurs qui en C++ auraient pu mener à des bugs, voire à des failles de sécurité potentielles.

En outre, Rust amène également au monde de la programmation système et impérative certaines fonctionnalités récentes issues de la programmation fonctionnelle : types algébriques, filtrage par motif (pattern matching), fermetures lexicales (closures), etc.

En bref, Rust se veut un remplaçant à C++ : moins complexe, plus sûr et plus moderne.

Rust étant développé au sein de Mozilla, on peut se demander s’il est prévu que Rust soit utilisé dans FirefoxOS, ce n’est pas encore le cas, bien que des essais aient été faits.

Exemple

Voilà un exemple d’une implémentation simpliste de ls :

use std::os;

fn list_dir(path: &Path) {
    match std::io::fs::walk_dir(path) {
        // Le chemin donné par l’utilisateur n’est pas valide
        None => fail!("Unable to retrieve path: {}", path.display()),

        // Le chemin donné par l’utilisateur est valide, on liste les fichiers dedans
        Some(dir) => for file in dir {
            println!("{}", file.display());
        }
    }
}

fn main() {
    // On récupère les arguments passé via la ligne de commande
    let argv = os::args();

    // On vérifie que le nombre d’arguments donnés est bien conforme à ce qu’on attend.
    match argv.len() {
        // S’il n’y a pas d’argument, on utilise le répertoire courant comme répertoire à traiter
        1 => list_dir(&os::getcwd()),

        // S’il y a un argument, on le considère comme répertoire à traiter.
        2 => list_dir(&Path::new(argv[1])),

       // Sinon, on considère que l’utilisateur tape avec des moufles.
        _ => {
            println!("You must specify exactly zero or one path");
            os::set_exit_status(1);
        }
    }
}

La communauté Rust

Rust est développé par Mozilla, qui emploie une équipe à plein temps pour travailler sur le langage, mais aussi par de très nombreux contributeurs : de 51 pour la version 0.1, 450 pour la 0.10 à 559 pour cette dernière version. Des statistiques plus détaillées sont disponibles sur le site RustStat.

Plusieurs moyens de communication existent sur Internet :

Rust a aussi des pages sur Google Plus et sur Facebook, ainsi qu’un mot-clé #rustlang, mais il n’y a pas d’activité particulière dans ces pages ; ce sont des rappels de Reddit.

On peut aussi remarquer que désormais LinuxFr.org fait partie de la mouvance avec sa propre étiquette rust ;) qui permet de retrouver facilement toutes les dépêches et tous les journaux abordant ce sujet.

Les échanges se font aussi directement lors d’évènemements.

Installation

Le tutoriel officiel détaille l’installation sur les différentes plateformes, cependant il faut aller dans le wiki pour avoir plus de détails.

Les procédures d’installation déjà en place pour la version 0.8 (pour Windows, Ubuntu, Arch Linux et Gentoo) décrites dans la dépêche ad hoc sont toujours valables et ont été mises à jour pour cette version. Dès l’annonce, les paquets pour Ubuntu ont été générés dans le PPA hansjorg/rust, une compilation pour Arch est disponible dans le dépôt community et pour Gentoo, l’overlay rust contient un ebuild pour cette version.

Voici rapidement les commandes à utiliser pour installer la dernière version :

# Sous tout type de Linux 64bits
curl -O http://static.rust-lang.org/dist/rust-0.11.0-x86_64-unknown-linux-gnu.tar.gz
tar xfz rust-0.11.0-x86_64-unknown-linux-gnu.tar.gz
(cd rust-0.11.0-x86_64-unknown-linux-gnu/ && sudo ./install.sh)
# Sous Arch, disponible dans les paquets officiels
# Sous Gentoo
sudo layman -a rust
sudo emerge --autounmask =rust-0.11.0
# Sous Ubuntu
sudo add-apt-repository ppa:hansjorg/rust
sudo apt-get update
sudo apt-get install rust-0.11

Pour Fedora, vous pouvez compiler en Rust en regardant ici ou .

Changements du langage

De nombreux changements de syntaxe, de sémantique, de bibliothèques sont advenus depuis la dernière version. Par exemple, le développeur de Angolmois a dénombré les changements qu’il a dû apporter à son code entre chaque version, depuis la 0.6. Moins de changements de code ont été nécessaires entre la 0.10 et la 0.11.

Syntaxe

Le symbole ~ a été remplacé par le type Box<T> pour dénoter les owned boxes, et par le mot-clé box pour l’allocation. De plus, les tableaux ~[T] et les chaines ~[str] ont été remplacées respectivement par Vec<T> et String. Comme il s’agit d’un changement très important et souvent mal compris par la communauté, voici une brève explication :

La précédente syntaxe causait de nombreuses confusions, du fait que ~[T] et ~str étaient en fait des structures particulières, et non pas de simple pointeurs ~ vers [T] ou str, ces derniers n’étant pas des types en Rust. Ceci menait à la bizarrerie syntaxique suivante :

// x est de type ~str, une chaîne allouée sur le tas, redimensionnable
// du fait qu’elle possède la mémoire pointée
// de même, y est de type ~[T], un vecteur redimensionnable alloué sur le tas
let x = ~"foo";
let y = ~[1, 2, 3];

// x est de type ~(&'static str), un pointeur vers une zone allouée sur le
// tas contenant un pointeur vers une chaîne statique
// y est de type ~([T, .. 3]), un pointeur vers une zone allouée sur le tas
// contenant un tableau statique à 3 éléments
let x = ~("foo");
let y = ~([1, 2, 3]);

Les structures ~[T] et ~str étaient en fait des structures situées sur la pile, contenant 3 champs :

  • un pointeur vers une zone mémoire sur le tas ;
  • la taille de cette zone, ou « capacité » ;
  • et la partie de cette zone occupée par des données valides, soit la longueur de la chaine ou du tableau.

Ces structures implémentent des chaines et des tableaux dits « redimensionnables », car lors d’un ajout, si la capacité n’est plus suffisante, on met à jour le pointeur vers une zone plus grande, et on recopie depuis l’ancienne. Puisque ces structures possèdent leurs données et disposent de sémantiques de déplacement, personne ne possède de pointeur sur l’ancienne zone, qui peut alors être libérée en toute sûreté.

La sémantique de Rust étant devenue suffisamment puissante pour permettre d’exprimer ces propriétés sur des types tiers, ces structures ne nécessitent plus en aucun cas d’être intégrées au compilateur, et ont été déplacées vers la bibliothèque standard, sous les noms de String et de Vec<T>. On peut d’ailleurs noter que String n’est qu’une couche d’abstraction sur Vec<u8> fournissant des méthodes supplémentaires pour garantir que les données sont une chaîne UTF-8 valide.

De même, la syntaxe ~ pour représenter un simple pointeur vers le tas possédant la zone pointée (owned box, ou owning pointer), a été remplacée par Box<T> pour désigner le type et par l’expression box foo réalisant une allocation dynamique et renvoyant un Box<T>.

Cette syntaxe n’ayant plus rien en commun avec Stringet Vec, il n’y a plus de confusion possible. Si foo est de type T, box foo renverra toujours un Box<T> (équivalent de l’ancien ~T), de même que box (foo).

  • let x = box "foo"; // x est de type Box<&'static str>.
  • let x = box ("foo"); // pareil.
  • let x = box 5; // x est de type Box<int>.

Si l’on désire maintenant utiliser les chaînes et les tableaux redimensionnables, il faut être explicite et utiliser les noms String et Vec :

// la méthode from_str alloue une zone de mémoire suffisamment grande
// et y recopie le contenu d’une autre chaine, ici une chaine statique
// foo est de type String
let foo = String::from_str("foo");

// from_slice alloue une zone de mémoire suffisamment grande et y recopie
// le contenu d’un autre tableau, ici un tableau statique
// bar est de type Vec<int>
let bar = Vec::from_slice([1, 2, 3]);

// on pourrait également utiliser cette version, équivalente, qui utilise
// la macro vec!, fournissant une façon pratique d’initialiser des Vec
let bar = vec!(1, 2, 3);

Cette nouvelle syntaxe montre bien que String et Vec sont en fait des structures sur la pile référençant une zone mémoire redimensionnable sur le tas.

Ces types sont munis d’un destructeur, ce qui garantit qu’ils disposent de déplacement (move semantics) et qu’ils possèdent les données pointées.

Ceci évite donc la confusion entre ces types et Box<T> qui n’ont rien à voir entre eux.

Par ailleurs, écrire let x = ~"foo" pouvait faire oublier au programmeur qu’il s’agissait d’une opération couteuse. On avait l’impression qu’il suffisait de prendre un pointeur sur une string, alors qu’il faut en fait allouer une zone sur le tas de la bonne taille, puis recopier la chaine. String::from_str est plus explicite, d’autant plus qu’elle est documentée, du fait qu’elle soit dans la bibliothèque standard et non une fonctionnalité intégrée au langage.

Enfin, bien que ~[] et ~str soient encore disponibles pour le moment, ils devraient être supprimés rapidement. Leur utilisation dans la base de code du compilateur et de la bibliothèque standard a déjà été pratiquement supprimée.

Notons que le pointeur @ qui a été placé derrière une feature gate (il devait être activé explicitement pour pouvoir être utilisé) lors de la sortie de rust 0.10 a été lui aussi été complètement supprimé.

Unboxed closures

Le système de fermetures (ou closures) de Rust a été complètement repensé.

Rust offrait actuellement deux types de fermetures : les stack closures et les « procédures ».

Les stack closures, qui capturaient les variables libres depuis leur environnement par référence. Ce type de fermetures était très utilisé dans du code Rust, mais du fait qu’elle ne possédaient pas leur environnement, elles n’étaient pas à proprement parler des valeurs de première classe. En particulier, il n’était possible de renvoyer une clôture sur la pile (stack closure) qu’à condition de la lier à une durée de vie (lifetime).

// la fonction get_adder prend un entier et renvoie une fermeture
// additionnant cet entier (capturé) à un autre passé en argument
fn get_adder(x: uint) -> (|uint| -> uint) {
    // on renvoie une fonction prenant y et renvoyant x + y
    // x est ici capturé par référence
    |y| { x + y }
}

fn main() {
    let add5 = get_adder(5);
    let r = add5(3);
}

Ce code produisait une erreur du borrow-checker. Si ce code avait été exécuté, l’appel de add5 aurait tenté d’accéder à x via une référence pointant vers une zone mémoire qui n’est plus valide, x étant détruit à la fin de la fonction get_adder. On aurait pu paramétrer les types par des lifetimes et passer x à get_adder par référence, mais cela n’aurait pas totalement résolu le problème. Il n’aurait en aucun cas été possible d’utiliser add5 en dehors de la portée des variables capturées (ici, x).

Les « procédures » (notées proc), qui capturaient leur environnement par copie (en effectuant éventuellement un move). Ces fermetures étaient des valeurs de premier ordre, mais elles permettaient ensuite au code de la fermeture d’effectuer un move depuis les variables capturées au moment où la fermeture était exécutée. Ceci permettait à ces fermetures d’utiliser leur environnement de n’importe quelle façon, mais ne permettait de les appeler qu’une seule fois, car leur environnement était « consommé » par l’appel de la fermeture, du fait des moves possibles.

fn get_proc(x: uint) -> (proc(uint) -> uint) {
    proc(y) { x + y }
    // x a été capturé par valeur. Si x avait été
    // doté de move semantics, toute utilisation de
    // x ici aurait été une erreur car il aurait été
    // déplacé dans l’environnement de la fermeture
}

fn main() {
    let add5 = get_proc(5);
    let huit = add5(3);

    // erreur: l’appel de la procédure prend l’environnement par move
    // rustc détecte ici l’utilisation d’une valeur déplacée et émet
    // une erreur. Les procs ne sont donc appelables qu’une seule fois
    let neuf = add5(4);
}

La proposition des unboxed closures rend le système de fermetures bien plus souple.

Toutes les fermetures captureront désormais leur environnement par copie. Les références étant des valeurs comme les autres en Rust, il sera toujours possible de capturer par référence en capturant explicitement une référence plutôt que la variable :

fn get_adder(x: uint) -> (|uint| -> uint) {
    // x est ici capturé par valeur (copie). Si x avait été
    // d’un type doté de sémantiques de déplacement, il n’aurait
    // plus été utilisable autrement que par la fermeture car
    // il aurait été déplacé vers son environnement, tout comme
    // dans l’exemple avec proc
    |y| { x + y }
}

// il est possible d’obtenir une capture par référence en capturant
// explicitement une valeur dont le type est une référence. On fait
// alors une copie de la référence
// cet exemple reproduira le comportement précédent. La fermeture
// est liée à la durée de vie de x
fn get_adder_ref(x: uint) -> (|uint| -> uint) {
    // on crée explicitement une référence
    let ref_on_x = &x;

    // la fermeture capture une copie de la référence
    // et pas une copie de x
    |y| { *ref_on_x + y }
}

Les unboxed closures seront implémentées comme des objets ayant chacun leur type unique contenant leur environnement. Pour refléter leur capacité à être invoquées comme des fonctions, elles devront maintenant implémenter un trait. Plusieurs traits seront introduits, pour représenter tous les cas possibles d’utilisation.

Tout d'abord, Fn : au moment de l’appel, le code reçoit son environnement via une référence mutable. La fermeture peut donc muter son environnement, mais ne peut pas effectuer de move depuis l’environnement. La fermeture est donc appelable plusieurs fois. Ce trait reflète les sémantiques d’appel des anciennes stack closures, tout en permettant de choisir au moment de la création de la fermeture si la copie se fait par copie ou par référence.

FnShared est similaire au précédent, à l’exception que la fermeture reçoit une référence immutable sur son environnement. Elle ne peut donc pas le modifier. L’avantage principal de ces fermetures est qu’elles peuvent être échangées de façon sûre entre différentes tâches s’exécutant de façon concurrente, d’où son nom.

Enfin, FnOnce implémente des sémantiques d’appel des anciennes procédures en passant à la fermeture son environnement par valeur. La fermeture peut alors librement effectuer des moves depuis l’environnement, qui est alors consommé. Une fermeture ne pourra être appelée qu’une seule fois via ce trait.

La syntaxe des fermetures ne sera donc plus que du sucre syntaxique sur la déclaration d’un type implémentant le trait approprié et la création d’une valeur unique de ce type. Mais il sera également possible de créer à la main de tels objets, et d’implémenter par exemple plusieurs comportements possibles. (à vérifier)

Le système de types de Rust ayant connaissance des sémantiques de déplacement, l’implémentation des unboxed closures ne requerra pratiquement aucune modification du système de types. Les règles actuelles garantissent déjà le comportement décrit ci-dessus en fonction de la façon dont l’environnement est passé au code de la fermeture lors de l’appel.

Bibliothèque standard

La bibliothèque standard a été découpée en plusieurs petites bibliothèques indépendantes. libcore, qui contient les fonctionnalités les plus basiques de la bibliothèque d’exécution (runtime library) du langage est ainsi autonome, et peut être utilisée dans des contextes tels que les systèmes d’exploitation ou la programmation embarquée.

Rust dispose maintenant d’une implémentation des expressions rationnelles, inspirée de RE2. Elle a été intégrée dans la distribution officielle en tant que libregex. libregex_macros fournit une extension de syntaxe, regex!, qui permet à rustc de compiler les expressions rationnelles en même temps que le reste du code.

En vrac, quelques autres modifications notables :

  • attention ! test::BenchHarness a été renommée en test::Bencher ;
  • la définition des vecteurs doit être plus précise : [1, 2] n’est plus acceptée, il faut spécifier le type, par exemple ainsi [1u, 2] ;
  • utilisation de Result<T, Error> dans les types de retour de l’interface Serialize ;
  • ajout d’une caisse GraphViz ;
  • réduction de la taille des exécutables ;
  • si vous vous demandez ce que Rust et Lovecraft on en commun, allez voir par ici ;
  • première bibliothèque stabilisée pour la version 1.0 : std::mem.

Autour du langage

Le code du dépôt Rust sur Github (compilateur, bibliothèque standard, tests unitaires et de performance ainsi que les tutoriels) a atteint les 30 000 commits le 25 juin. Voici un florilège des évènements notables :

Computer Language Benchmarks Game

Ce test de performance dont nous avions parlé dans la précédente dépêche sur Rust continue d’être mis à jour. Les sources des programmes de test sont en effet inclus dans les sources même de Rust (voir les fichiers shoutout-*). Les instructions SIMD sont ajoutées aux tests à l’aide du module std::unstable::simd, le test shootout-mandelbrot s’exécute ainsi presque deux fois plus vite.

Test × CPU secs Elapsed secs Memory KB Code B ≈ CPU Load
Fasta 1.7 4.66 4.66 780 1283 0% - 1% - 1% - 100%
Pidigits 7.2 12.48 12.49 1,708 677 0% - 1% - 0% - 100%
Mandelbrot 10 52.22 52.23 780 633 1% - 100% - 0% - 0%

http://www.reddit.com/r/rust/comments/27dc75/what_happened_to_the_shootout_benchmarks/

Travis-CI

Les tests sur Travis-CI utilisant rust-nightly sont restés bloqués à la version du 18 avril. Travis-CI utilise Ubuntu Precise comme environnement et la construction automatique sur Launchpad s’est arrêtée car la version de gcc est trop ancienne. Le script configure de Rust ne prenait pas en charge la définition des variables d’environnement CC/CXX pour changer de version de compilateur. Des correctifs ont été proposés le 2 mai et intégrés depuis. La construction continue chez Launchpad a repris début juin.

Cargo

Cargo est le nouveau gestionnaire de paquets annoncé le 17 mars. Yehuda Katz et Carl Lerche réalisent son développement.

Le 21 juin, le gestionnaire n’est pas encore en version alpha mais permet de résoudre les dépendances et de les récupérer depuis Git. La version alpha est publiée le 23 juin en même temps que le site web associé pour sa documentation : crates.io. Un PPA pour Ubuntu est disponible à ppa:cmrx64/cargo, pour notamment pouvoir utiliser Cargo sur Travis-CI.

Servo

Servo est un projet expérimental de Mozilla visant à construire un moteur de navigateur Web pour la nouvelle génération d’appareils : téléphones portables, processeurs multicœurs et GPU haute-performance, en tirant parti de la sûreté de Rust, et de ses facilités pour exprimer la concurrence. Il est actuellement développé pour Mac OS X et Linux 64 bits. Il a récemment passé avec succès le test Acid2, comme planifié dans les objectifs du second trimestre.

Servo: Designing and Implementing a Parallel Browser.

Liens

Notes de version.

Récapitulatifs

This Week in Rust

Si vous voulez suivre le mouvement de tout ce qui se passe à propos de Rust sans avoir à lire le détail des commits, des annonces sur la liste de diffusion, de Reddit ou de Twitter, le blog This Week in Rust fait une synthèse hebdomadaire des nouveautés et actualités autour de Rust :

Meeting Weekly

https://github.com/mozilla/rust/wiki/Meetings

Évènements

De nombreux évènements sont organisés autour de Rust. La rencontre parisienne se répète tous les 3es lundis du mois dans les locaux de Mozilla.

  • Paris, le 21 avril — Rust MeetUp ;
  • Londres, du 25 au 28 avril — Ludum Dare 29 ;
  • San Francisco, le 8 mai — Rust MeetUp : vidéos disponibles sur air.mozilla : Testing Rust and Fuzzing compilers de John Regehr, QuickCheck de Andrew Gallant et Testing Hackathon de Erick Tryzelaar ;
  • Paris, le 19 mai — Rust MeetUp, sur Servo ;
  • Pittsburgh, le 19 mai — Rust MeetUp : Code and Supply ;
  • Seattle, le 22 mai — Rust MeetUp  ;
  • Paris, le 16 juin — Rust MeetUp ;
  • San Francisco, le 10 juin — Dinnerup ;
  • Brooklyn, le 21 juin — Rust MeetUp ;
  • Pittsburgh, le 23 juin — Rust MeetUp : Code and Supply ;
  • Londres, le 26 juin — First Rust MeetUp in London : Awesome Rust, Servo: the parallel browser engine ;
  • San Francisco, le 26 juin — Rust Meetup, vidéos disponibles sur air.mozilla.org : Library Ecosystem for Rust Game Development, OpenGL and Rust, Voyager, Reducing VR latency with Rust ;
  • Lisbonne, le 2 juillet — Rust MeetUp : Goals and crash course through the tutorial ;
  • San Francisco, juillet — Rust Meetup : WebTech ;
  • Seattle, le 7 juillet — Rust MeetUp ;
  • Hanovre, le 10 juillet — Rust MeetUp ;
  • Paris, le 21 juillet — Rust MeetUp ;

Présentations

Il y aura peut-être bientôt des cours de Programmation Fonctionnelle Système en Rust à Mozilla Paris.

Tutoriels et documentation

Projets

Nouveaux projets

Conclusion

La liste des améliorations pour cette version de Rust n’est pas bien longue: modification des types Vectors, et les Strings en préparation des types à taille dynamique (DST), la suppression de ~ et continuation du découpage de la bibliothèque standard de Rust. Tout cela a nécessité beaucoup de travail de fond, et c’est le signe que Rust gagne en maturité.

Côté communauté, on a des développeurs payés par Mozilla et Samsung qui travaillent sur Rust et Servo, des dizaines de nouveaux projets, la prise en charge de Rust dans de plus en plus de logiciels et d’environnements, et une présence sur le web toujours plus importante, Rust semble promis à un bel avenir.

  • # Bravo pour la dépêche

    Posté par (page perso) . Évalué à 7.

    Du beau boulot. Plein de nouveaux onglets ouverts dans mon navigateur en fin de lecture. :)

    Adhérer à l'April, ça vous tente ?

  • # GitHub

    Posté par . Évalué à 10.

    le code de Rust est désormais hébergé sur Github

    Pour être plus précis, le projet Rust est depuis très longtemps sur GitHub. Ce qui a récemment changé, c'est qu'il faisait partie du groupe GitHub mozilla, et qu'il a été déplacé dans son propre groupe rust-lang. On trouve dans ce groupe tous les dépots liés à Rust : Cargo, le site web de rust-lang.org, les RFCs, les dépendances du compilateur, etc…

    • [^] # Re: GitHub

      Posté par (page perso) . Évalué à 4.

      Oui c'est visiblement une erreur. L'idée originale, c'était:

      Le projet Github Rust était hébergé par le compte mozilla; il est désormais hébergé sur le compte rust-lang.

      Écrit en Bépo selon l’orthographe de 1990

  • # Forum officiel Rust

    Posté par . Évalué à 7.

    En plus des différents lieux cités (mls, IRC…) il existe maintenant un forum officiel : discuss.rust-lang.org.

    Personnellement j'aurais préféré une ml, je hais les forums (push vs pull).

  • # boxed/unboxed ou expansion

    Posté par . Évalué à 1.

    Je n'ai pas bien compris l'usage de Box, est-ce qu'il s'agit du même système d'expand de Lisaac, qui permet d'éviter les cochonneries de type natif de Java (int != Integer) ?

    Dans Lisaac, c'était hyper puissant, de faire la différence entre un layout mémoire "en dure" et un arbre de pointeur tel que se présente une arborescence d'objet. Typiquement, cela permettait à l'OS Isaac de coder les pixels de l'interface graphique comme des objets, sans perte de vitesse par rapport au C.

    "La première sécurité est la liberté"

    • [^] # Re: boxed/unboxed ou expansion

      Posté par (page perso) . Évalué à 3.

      Ça n'a rien à voir, Box<> en Rust, c'est une allocation sur le tas.

      « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

      • [^] # Re: boxed/unboxed ou expansion

        Posté par (page perso) . Évalué à 2.

        Pourquoi avoir choisi ce mot box?

        http://devnewton.bci.im

        • [^] # Re: boxed/unboxed ou expansion

          Posté par (page perso) . Évalué à 3.

          Parce que boxed et unboxed c’est le vocabulaire utilisé également par d’autres langages de programmation. D’autre part, ça montre bien la façon de fonctionner de Rust: lorsque la boite est supprimée, la valeur qui était à l’intérieur (boxed) est supprimée également. Et si on veut récupérer la valeur qui est à l’intérieur, on fait une copie à l’extérieur de la boite (on obtient une valeur unboxed).

          Écrit en Bépo selon l’orthographe de 1990

          • [^] # Re: boxed/unboxed ou expansion

            Posté par (page perso) . Évalué à 1.

            Quels langages utilisent cette sémantique ?

            • [^] # Re: boxed/unboxed ou expansion

              Posté par . Évalué à 2.

              C++ mélange un peu tout(transmission par valeur = expansion, transmission par pointeur = box).

              Cela détermine qui gère la mémoire. La destruction d'un arbre d'objet "boxed" nécessite un GC pour savoir ce qui peut être réellement effacer. Une structure en expension avec les valeurs dedans sont détruit ensemble d'un seul coup.

              En java int n'est pas boxer, Integer l'est. Cela permet par exemple d'utiliser des conteneur qui utilise le type de base Object, qui ne peut pas prendre un int. Cette blague existe pour tous les types de base.

              En Ocaml, la box est complètement masquée. Les integer et les pointeurs sont codé avec un bit pour les distinguer, le reste est boxé. Le compilo peut savoir unboxer automatiquement les flottant et les tableaux de double. La box ne fait pas trop de perte de performance en Ocaml, car par défaut, une valeur n'est pas modifiable, on peut ainsi utiliser le pointeur sur la donné de base à la place, sans risque.

              "La première sécurité est la liberté"

              • [^] # Re: boxed/unboxed ou expansion

                Posté par (page perso) . Évalué à 1.

                Bon, du coup je ne comprends toujours pas cette notion.

                En C++, il y a deux natures de variables : les automatiques, les dynamiques. Les variables automatiques sont automatiquement allouées lors de l'exécution du code. En général, ce sont les variables de porté locale, et sont allouées sur la pile :

                void fonction()
                {
                  auto int automatic = 5;
                }

                Les variables dynamiques sont elles allouées à l'aide de new (ou malloc, ou mmap, ou équivalent) :

                void fonction()
                {
                  auto int * automatic = new int(5);
                  delete automatic;
                }

                On remarquera que dans cet exemple, une variable automatique est nécessaire pour contenir l'adresse de l'objet instancié.

                Ensuite tu as std::auto_ptr, std::shared_ptr, std::unique_ptr, boost::scoped_ptr qui permettent de gérer automatiquement la libération de l'objet quand l'objet n'est plus référencé.

                Bref, qu'est-ce qui est « boxé », et en quoi l'objet est en boîte ou non ?

      • [^] # Re: boxed/unboxed ou expansion

        Posté par (page perso) . Évalué à 1.

        Donc si je comprends bien, Box<> est l'inverse du Expanded. Un type Expanded est un type sur la pile et un type non Expanded correspond a un pointeur vers une zone mémoire sur le tas (équivalent du Box<> donc)

        • [^] # Re: boxed/unboxed ou expansion

          Posté par . Évalué à 2.

          "Boxed" est le terme classique notament en java. Cela veut dire que chaque objet est dans sa propre zone mémoire, est n'est inclus dans d'autre structure qu'uniquement au travers de pointeurs. Dans le cas de petit objet, c'est une catastrophe pour les performances. "Expended" permet de rester full objet, mais sans la "box" (et une restriction sur l'héritage dynamique aussi).

          Expended n'est donc pas uniquement une allocation sur la pile.

          "La première sécurité est la liberté"

    • [^] # Re: boxed/unboxed ou expansion

      Posté par (page perso) . Évalué à 2. Dernière modification le 15/07/14 à 14:58.

      Je n'ai pas bien compris l'usage de Box, est-ce qu'il s'agit du même système d'expand de Lisaac, qui permet d'éviter les cochonneries de type natif de Java (int != Integer) ?

      En Rust, les types natifs ont les mêmes propriétés que les objets (on peut leur implémenter des méthodes) sans surconsommation mémoire ou processeur. Il est donc possible d’avoir une variable de type Box<int>, qui est un pointeur vers un entier alloué sur le tas.

      Écrit en Bépo selon l’orthographe de 1990

    • [^] # Re: boxed/unboxed ou expansion

      Posté par . Évalué à 3.

      Pourquoi t'exprimes-tu à l'imparfait ?

      Lisaac est-il mort ?
      Plus de troll sur le meilleur langage du monde … Sniff !!!

  • # Un peu de bike shedding: je n'aime pas leur syntaxe des format string

    Posté par . Évalué à -1.

    et je trouve franchement dommage qu'un nouveau langage utilise une syntaxe si peu lisible..
    
    L'équivalent de println!("{}", foo); ça serait en Perl print("$foo");
    ou avec des {} si on veut clairement délimiter les variables du texte print("${foo}"); en Ruby pareil mais avec un # au lieu d'un $.
    
    Dans les deux cas en Perl ou en Ruby, quand on lit le programme pas besoin de faire des aller-retour entre le texte et les variables contrairement à Rust ou au C: qui ne s'est jamais trompé en C dans ses chaine de formats en utilisant la mauvaise variable?
    Pas moi!
    
    En Rust, on peut faire  println!("{u}", u = foo);, c’est un peu moins moche mais je ne comprends pas l'intérêt par rapport à un (hypothétique) println!("{foo}"); ??
    
    • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

      Posté par (page perso) . Évalué à 2.

      Je pense que c’est majoritairement en rapport avec l’internationalisation. On peut vouloir changer le nom des variables parfois, ou la façon dont on accède à un nom par exemple (dans un cas ça sera directement une chaine, dans l’autre une entrée d’un tableau…). On peut vouloir changer la valeur qui apparaitra dans la chaine sans changer la variable, dans ce cas-là soit on met le calcul dans la chaine dans ta proposition, soit on créé une nouvelle variable. Pour une traduction, c’est plus facile d’avoir un nom signifiant qu’un nom de variable (avec éventuellement un calcul abscons).

      Pour plus d’infos sur les possibilités, voir la doc.

      Écrit en Bépo selon l’orthographe de 1990

      • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

        Posté par . Évalué à 2.

        Euh, il me semble que tout ce que tu dis, on peut le faire en Perl avec en plus l'avantage que tu as une syntaxe lisible pour les programmes tout simple ou tu n'as pas l'internationalisation..

        De plus je ne vois pas l'avantage de faire printf!("{u}, u = v); par rapport à
        my $u = $v;
        print("$u");

        • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

          Posté par (page perso) . Évalué à 3.

          Je trouve intéressant de pouvoir faire

          println!("plop: {u}", u = ma_fonction_un_peu_longue())

          Je trouve ça plus lisible. Et je trouve aussi plus lisible de pouvoir n'avoir que {}, ça permet de distinguer plus facilement la partie statique de la partie variable.

          « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

          • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

            Posté par . Évalué à 1.

            Pouvoir faire ça, ok ça peut être intéressant, mais ne pouvoir faire QUE ça, c'est ça qui me choque, pourquoi je ne peut pas faire println!("plop: {ma_variable}");
            au lieu de devoir faire println!("plop: {u}", u = ma_variable);

            • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

              Posté par (page perso) . Évalué à 4.

              Ça me semble normal si on ne veut éviter les variables globales. En fait la chaîne est passée à la fonction fmt qui ne connait bien sûr pas les variables déclarées, c'est donc normale de spécifier quelles variables doivent être passées.

              « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

              • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

                Posté par . Évalué à 3.

                En fait la chaîne est passée à la fonction fmt qui ne connait bien sûr pas les variables déclarées,

                Note que ce n'est pas une fonction, c'est une macro donc comme c'est a la compilation, je pense qu'on peut extraire le nom de la variable de la chaine et c'est le compilateur qui hurle plus tard si la variable n'est pas déclarée..
                En D, un mixin doit pouvoir faire ça.

        • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

          Posté par . Évalué à 3.

          En vrac :
          - comme l’a dit sinma, dans le cas de l’internationalisation, tu rends ta chaîne dépendante d’un paramètre nommé qui lui est indépendant de ton code -> couplage faible, évolution plus facile.
          - en cas de refactorisation de code, tu peux changer la manière dont est calculée la variable sans changer la chaîne.
          - toujours dans le cadre d’une refactorisation de code, il sera beaucoup plus facile pour l’analyseur de déterminer quand et où est utilisée une variable que si elle peut apparaître n’importe où dans une chaîne.

          Rust est conçu et pensé pour permettre de vérifier un maximum de chose dès la compilation. On est à des années lumière de la philosophie de perl de ce côté là. La syntaxe des chaînes en fait partie. Libre à toi de la trouver contraignante, personnellement elle ne me choque pas. Les placeholders nommés, c’est même un gain par rapport à ce qu’on a dans la plupart des langages.

          Mes commentaires sont en wtfpl. Une licence sur les commentaires, sérieux ? o_0

          • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

            Posté par . Évalué à 4.

            La plupart des langages?
            Ça ne me parait pas un argument valide pour un nouveau langage, il devrait autant que possible reprendre le meilleur des langages existants!

            Ah, j'imagine que certains trouvent la comparaison de Rust avec Perl ou Ruby biaisée car ces derniers sont interprétés, mais avec Scala la syntaxe est similaire..

            Qui préfère lire
            println!("texte {}", variable) ou println!("texte {v}, v = variable); (Rust)
            a
            println(s"texte $variable") ou println(s"texte ${variable}") (Scala)
            ?

            Quand le nombre de variables augmente, je trouve la version Scala/Perl/Ruby bien plus lisible..

      • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

        Posté par (page perso) . Évalué à 2.

        Ce n'est pas pour l'internationalisation. «print!» est une macro (comme le point d'exclamation l'indique) qui va vérifier que les paramètres de la chaine de formatage sont corrects. Par exemple avec "{0} {1}" il doit y avoir deux paramètres additionnels. Cette vérification est faite à la compilation (ce qui est bien, comme ça ça n'implique pas un surcout d'analyse de la chaine à l'exécution).
        Par contre, on est obligé de mettre une chaine littérale, sinon:

        error: format argument must be a string literal.

    • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

      Posté par . Évalué à 1.

      Ah ?! Moi c'est leur x::y::z que je ne peux pas voir en peinture :).

      • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

        Posté par (page perso) . Évalué à 1.

        Sinon on peut continuer à lister toutes les choses que vous n’aimez pas dans Rust au lieu de ce concentrer ce sur quoi il est vraiment innovant (le prend pas pour toi, hein).

        Hé oui, la syntaxe d’un langage ne peut pas être parfaite. Mais globalement, soit ils reprennent une syntaxe existante, soit ils en inventent une qui a du sens par rapport aux objectifs du langage.

        Écrit en Bépo selon l’orthographe de 1990

    • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

      Posté par . Évalué à 1.

      Python utilise une syntax similaire et elle est effectivement très puissante:

      "toto {tata}".format(tata="tutu")

      • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

        Posté par . Évalué à 1.

        Puissante, puissante.. Ça se discute.
        Je ne vois pas trop en quoi c'est plus puissant que l'équivalent en Scala (la même chose existe en Perl, Ruby):
        tata="tutu"
        "toto ${tata}"
        mis à part que la syntaxe Python/Rust viole la règle DRY (ne pas se répéter)..

        Si tu as une variable avec un nom bien clair alors "toto ${ma_variable}" est court et bien lisible mais la syntaxe Python/Rust impose d'ajouter un renommage supplémentaire même dans ce cas ("toto {foo}",foo=ma_variable)..

        • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

          Posté par (page perso) . Évalué à 2. Dernière modification le 21/07/14 à 22:58.

          Ça permet de passer toutes les variables dans un dictionnaire, sans forcément les connaître à l'avance. De plus, elle permet un formatage supplémentaire grâce aux indications (nombre de chiffres, remplissage avec des espaces, …). Petit exemple rapide en Python

          mes_variables = {'ma_variable': 42, 'foo': -1}
          "toto %(ma_variable)s %03(foo)d" % mes_variables
          La syntaxe "toto ${ma_variable}" impose que ma_variable soit définie quand tu te sers de la chaîne de caractère. La syntaxe Python n'importe aucune contrainte sur les variables.

          • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

            Posté par . Évalué à 0.

            Je trouve toujours la syntaxe "toto #{ma_variable}" beaucoup plus lisible.

            La version python, c'est bien, mais je ne vois pas l'intérêt (l'exemple est trop court ;-) ), surtout face à la perte de lisibilité.

            "Quand certains râlent contre systemd, d'autres s'attaquent aux vrais problèmes." (merci Sinma !)

          • [^] # Re: Un peu de bike shedding: je n'aime pas leur syntaxe des format string

            Posté par . Évalué à 1.

            Ça permet de passer toutes les variables dans un dictionnaire, sans forcément les connaître à l'avance.

            Ce qui est rarement utile.. Rendre moins lisible et moins maintenable la syntaxe la plus utilisée pour une possibilité rarement utilisée, bof!

            De plus, elle permet un formatage supplémentaire grâce aux indications (nombre de chiffres, remplissage avec des espaces, …).

            Et alors? Avoir des formats strings lisibles n’empêche pas d'avoir des formatages supplémentaire..
            En Scala, ça donne ça: println(f"$name%s is $height%2.2f meters tall")

            J'ai l'impression qu'il y en a pas mal qui se disent, c'est comme ça que c'est fait en Python donc c'est forcément bien!
            Désolé mais si la lisibilité est subjective, la maintenable ne l'est pas elle et
            1) mettre les variables séparément des chaines ("foo {} faa {}",var1,var2) est moins maintenable que ("foo $var1 faa $var2")
            2) en utilisant les places holders nommés tu peux récupérer la maintenabilité ("foo {u} faa {v}",u = var1, v = var2) MAIS c'est au prix d'une verbosité et d'une redondance supplémentaire par rapport à ("foo $var1 faa $var2") (si le nom des variable est trop moche pour mettre directement dans la format string c'est un problème entre la chaise et le clavier, pas un problème de langage).

            Alors OK, les places holder nommés permettent quelques trucs supplémentaires utile dans 1% des cas, mais ça reste une syntaxe pas terrible pour le cas par défaut.

            De mon point de vue, les concepteurs de Python ont merdé sur ce point là, ça arrive et après c'est difficile de changer donc ils sont coincés OK, mais que les dev de Rust copient Python sans améliorer la syntaxe par défaut là..

  • # Et bha ...

    Posté par (page perso) . Évalué à 2.

    … ça m'a l'air tout de même beaucoup claire à lire que C++ quand même : )

    • [^] # Re: Et bha ...

      Posté par (page perso) . Évalué à 7.

      Même les résultats de l'IOCCC sont plus lisibles que du C++ moderne.

    • [^] # Re: Et bha ...

      Posté par . Évalué à 1.

      Effectivement, c'est beaucoup mieux.

      L'avantage du C++ sur les nouveaux langages compilés type Rust ou Go reste la portabilité. Ca passe partout.

    • [^] # Re: Et bha ...

      Posté par (page perso) . Évalué à 1.

      En quoi est-ce plus clair que du C++ ? Il y a bien plus d'abréviations cryptiques en Rust qu'en C++.

      • [^] # Re: Et bha ...

        Posté par (page perso) . Évalué à 2.

        Pourrais-tu préciser quels sont les «abréviations cryptiques» de Rust?

        Écrit en Bépo selon l’orthographe de 1990

        • [^] # Re: Et bha ...

          Posté par (page perso) . Évalué à 1.

          D'accord, ceux-là sont généralement assez bien compris dans le monde IT :

          • fs
          • std
          • dir
          • println

          Mais là, je me demande quel est le projet :

          • Vec : pour trois lettres?
          • fn : fininsh?
          • proc : processeur ? procédure ? proctologue ?
          • mut : mutable ? mutex ? mutton ?
          • iter : nucléaire ? pourquoi pas le bien connu "it" ?
          • use std::rc::Rc : realease candidate disease ?
          • use std::cell::RefCell : celllar ? Comment ça ce n'est pas une abréviation ?

          Bref, je n'aime pas les abréviations, car elles introduisent une incertitude. J'en utilises, évidement, mais en très petit nombre, et de préférences définies dans leur scope d'usage.

          Avoir des abréviations définies au niveau du langage est un appel aux utilisateurs du langage à en inventer d'autres. C en est plein, encore aujourd'hui des gens ont eu leur cerveau plié par cette convention qui avait une raison d'être. Oui, avait, nous ne sommes plus limités par des contraintes de place.

          • [^] # Re: Et bha ...

            Posté par (page perso) . Évalué à 5.

            Vec : pour trois lettres?
            fn : fininsh?
            iter : nucléaire ? pourquoi pas le bien connu "it" ?

            ces abréviations sont tout aussi bien comprises que les autres.

            « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

            • [^] # Re: Et bha ...

              Posté par (page perso) . Évalué à 1.

              Le problème n'est pas qu'elles ne peuvent pas être comprises : c'est qu'elles accrochent en première lecture.

              Rust est censé ne pas reproduire les erreurs du passé, rendre le code moins propices aux erreurs. Alors pourquoi employer des abréviations ?

          • [^] # Re: Et bha ...

            Posté par . Évalué à 1.

            Grâce à ça le compilo fait beaucoup moins d'io, c'est pour ça qu'il va si vite ;-) comparativement java est très lent au runtime parcequ'il gère des nom de classe très très longs (SaxParserHandlerFactoryFactoryProxyAdapter quand même… Et je vous épargne le nom du package :-D).

            • [^] # Re: Et bha ...

              Posté par (page perso) . Évalué à 2.

              Quel est le rapport avec la choucroute ? Tu crois sincèrement que tu gagnes significativement des IO en économisant 3 caractères sur un identifier ?

              Le monde Java, et par extension le monde des frameworks PHP, est terrifiant. Mais j'ose penser qu'il est possible d'avoir une voie médiane.

              De plus, en C++, la longueur d'un identifier n'a pas d'influence sur le runtime stripé. Mais Java tente de fournir des fonctionnalités inutiles mais appréciées qui nécessite la manipulation des identifiers de 10 km de long.

          • [^] # Re: Et bha ...

            Posté par (page perso) . Évalué à 2.

            Vec : pour trois lettres?

            Hé bien c’est quand même vachement utile quand tu déclares une variable de type Vec<Vec<Vec<int>>> (contre vector<vector<vector<int>>>> en C++).

            fn : fininsh?
            proc : processeur ? procédure ? proctologue ?
            mut : mutable ? mutex ? mutton ?

            En C/C++/Java on utilise même pas de mot-clé spécifique pour les fonctions, en Python on utilise def, etc. Globalement, fn est clairement le plus compréhensible.

            Et pour proc et mut c’est pareil, ce sont des mots-clés de base à apprendre comme dans tous les langages, pas de quoi en faire un fromage. Surtout qu’avec le contexte .

            Pour les autres, tu apprends une fois ce que ça veut dire et c’est bon, c’est juste que c’est pas aussi verbeux que dans les autres .

            iter : nucléaire ? pourquoi pas le bien connu "it" ?

            Plus facilement compréhensible qu’it sans être aussi long qu’iterator. Et ce n’est pas la première fois que je vois iter


            J’utilise des noms de variables en général assez long, mais pour le langage je n’ai pas besoin que les mots-clés et types les plus utilisés soient très long, au contraire je préfère qu’ils soient courts pour me faciliter la tâche.

            D’autre part, je n’ai pas énormément utilisé le langage mais ça ne m’a posé aucune difficulté. Pas plus que les abréviations telles que std, def, var, int, len, str, etc. Je pense que tu n’as pas testé et que si tu testerais, tu te rendrais compte que ce n’est pas absolument pas un problème.

            Écrit en Bépo selon l’orthographe de 1990

            • [^] # Re: Et bha ...

              Posté par (page perso) . Évalué à 1.

              Ah ben j'avais zappé ta réponse, désolé.

              Je ne trouve pas Vec<Vec<Vec<int>>> plus clair que vector<vector<vector<int>>>>, si au moins tu avais donné la version canonique std::vector<std::vector<std::vector<int>>>> ;) De toute façon, on ne déclare pas ça, c'est à encapsuler.

              Je ne vois pas trop l'intérêt d'un mot clé pour déclarer une fonction. Pour une classe/structure non plus d'ailleurs, mais bon.

              D’autre part, je n’ai pas énormément utilisé le langage mais ça ne m’a posé aucune difficulté. Pas plus que les abréviations telles que std, def, var, int, len, str, etc. Je pense que tu n’as pas testé et que si tu testerais, tu te rendrais compte que ce n’est pas absolument pas un problème.

              Le plus gros problème que ça me pose, c'est que ça donne un mauvais exemple à suivre.

              • [^] # Re: Et bha ...

                Posté par (page perso) . Évalué à 3.

                Je ne trouve pas Vec<Vec<Vec<int>>> plus clair que vector<vector<vector<int>>>>, si au moins tu avais donné la version canonique std::vector<std::vector<std::vector<int>>>> ;) De toute façon, on ne déclare pas ça, c'est à encapsuler.

                C’est quand même plus sympa de pas avoir une ligne qui fait trois kilomètres. Les tableaux étant une fonctionnalité tout de même assez basique, il est logique d’avoir une syntaxe qui va avec.

                En plus ton code il est faux, t’as mis un chevron en trop à la fin sur tes deux exemples. :P

                Je ne vois pas trop l'intérêt d'un mot clé pour déclarer une fonction. Pour une classe/structure non plus d'ailleurs, mais bon.

                Plus facile de rechercher avec grep, analyse syntaxique plus simple, plus logique.

                On cherche une fonction? cherchons un fn. On cherche une variable? cherchons un let. On cherche une structure? cherchons un struct.

                D’autre part, je n’ai pas énormément utilisé le langage mais ça ne m’a posé aucune difficulté. Pas plus que les abréviations telles que std, def, var, int, len, str, etc. Je pense que tu n’as pas testé et que si tu testerais, tu te rendrais compte que ce n’est pas absolument pas un problème.

                Le plus gros problème que ça me pose, c'est que ça donne un mauvais exemple à suivre.

                Les trucs courts pour le langage (quelques mots-clés/nom de fonctions couramment utilisés qui existent dans tous les langages et que tout le monde comprend), les trucs longs et explicites pour le développement ensuite (ce qui va nécessiter plus de détails, plus d’informations pour bien comprendre ce que le développeur fait).

                Après si le développeur fait n’importe quoi, on peut difficilement accuser la langage, qui essaie de mettre le moins d’entraves possibles entre le programmeur entre le programme. De plus, le tutoriel Rust — et n’importe quel bon tutoriel ou bouquin — n’encourage absolument pas à ce genre de pratique, au contraire.

                Écrit en Bépo selon l’orthographe de 1990

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.