Présentation de Rust 0.8

129
11
oct.
2013
Rust

Rust est sorti en version 0.8 le jeudi 26 septembre dernier, apportant comme d’habitude son lot de nouveautés.

Rust est un langage de programmation multi-paradigme (procédural, fonctionnel, orienté objet), compilé et orienté système. Il se veut donc un concurrent sérieux de langages tels que le C, C++, D et Go.

Logo Rust

Centré sur la sûreté, la concurrence et la praticité, il est développé par Mozilla Research (ils ne font pas que du web !) et une communauté de bénévoles. Il est publié sous double licence Apache 2.0 et MIT.

Il y a déjà eu deux dépêches complémentaires consacrées à Rust, à l’occasion de la sortie de la version 0.1 et de la version 0.3. Notons également un journal plutôt intéressant mais assez succinct, paru à l’occasion de la sortie de la version 0.7.

Mais en règle générale, le sujet reste borné à quelques blogs anglophones, quelques nouvelles furtives sur Developpez.com par exemple, et bien sûr le site web du projet.

Il me semblait donc nécessaire de faire le point sur ce langage à la communauté dynamique et qui semble très prometteur. La sortie de la version 0.8 est donc l’occasion rêvée pour vous faire une présentation complète du langage. D’ailleurs, ce document est sûrement, à la date de la publication, le plus gros document francophone concernant Rust (et la plus grosse dépêche jamais publiée sur Linuxfr ? :p).

Note de l’auteur : un grand merci à sebcrozet pour ses connaissances sur le fonctionnement de Rust (qui s’est inscrit sur Linuxfr juste pour l’occasion !), à olivierweb et à Olivier Renaud pour leurs innombrables corrections, ainsi qu’à tous les autres contributeurs bien entendu !

Sommaire

Qu’est-ce que Rust ?

Histoire

En 2006, Graydon Hoare commence un projet personnel, Rust, qui le restera pendant trois ans. À ce stade, le langage fonctionne assez bien pour faire tourner quelques exemples basiques. Il fut donc jugé suffisamment mature pour être pris sous l’aile de Mozilla.

Le compilateur était à l’origine écrit en OCaml, mais a été réécrit en Rust en 2010. On appelle cela un compilateur auto-hébergé parce qu’il est capable de se compiler lui-même. Le nouveau compilateur est basé sur l’excellente infrastructure LLVM, utilisée notamment au sein de Clang.

À terme, le langage devrait rivaliser en termes de vitesse avec du C++ idiomatique tout en étant plus sûr, et dépasser la vitesse du C++ à sûreté égale. En effet, l’écrasante majorité des vérifications de sûreté sont effectuées à la compilation, et il reste des tas d’optimisations à faire un peu partout. La sémantique du Rust (plus riche que celle du C++) permet en outre à LLVM de faire quelques optimisations supplémentaires.

Pour suivre les évolutions des performances de Rust, c’est par là.

Servo

On peut se demander pour quelle raison la fondation Mozilla a choisi d’investir dans le développement d’un nouveau langage. La raison est que les développeurs de Mozilla ont besoin de produire du code à la fois efficace, sécurisé, et parallélisable ; et le langage C++ qu’utilisent habituellement les développeurs Mozilla atteint rapidement ses limites sur ces deux derniers points. Plus particulièrement, Mozilla a commencé début 2012 à développer Servo, un moteur de rendu de pages web (HTML et CSS) dont les objectifs principaux sont justement la sécurité et la parallélisation. Servo est écrit en Rust, et par conséquent Rust a été fortement influencé par les besoins de Servo, puisque ces deux projets ont évolué ensemble. Cette situation n’est pas sans rappeler la symbiose qu’il y eu à l’époque entre le langage C et le projet Unix, qui ont été développés de concert.

L’architecture de Servo permet d’avoir de nombreux composants isolés (le rendu, la mise en page, l’analyse syntaxique du HTML, le décodage des images, etc) qui tournent en parallèle, pour obtenir un maximum de vitesse et surtout de stabilité. Le 3 avril dernier, Mozilla et Samsung ont annoncé leur collaboration pour développer ce projet.

Pour le moment, Mozilla n’a aucune intention d’utiliser Servo dans Firefox, car il est encore très loin d’être fonctionnel (d’après le wiki, encore un bug à corriger pour passer le test acid1 et plusieurs dizaines pour l’acid2), et aussi parce que ça demanderait beaucoup de travail pour l’intégrer au sein de Firefox.

Quels sont les buts du langage ?

Tout d’abord, c’est un langage plutôt orienté système (fonctionnalités de bas niveau, proches du matériel), mais avec une bonne sécurité par défaut (contrairement au C et au C++). La syntaxe du langage et les vérifications du compilateur empêchent énormément d’erreurs courantes. C’est simple : à long terme, il sera impossible de provoquer des fuites de mémoire (memory leaks), des dépassements de tampon (buffer overflow), ou des erreurs de segmentation (segfault) grâce à une gestion de la mémoire très bien pensée. Pour le moment, c’est juste très difficile !

C’est aussi un langage qui se parallélise aussi bien voire mieux que ce qui se fait dans les autres langages modernes. Il est facile de créer des tâches légères qui n’ont pas de mémoire partagée, mais un système de déplacement de variable d’une tâche à une autre.

Enfin, il réutilise des concepts connus et éprouvés, la « rouille » (rust), même s’il y a quand même quelques nouveautés. Néanmoins, certains langages, dont sont issus ces concepts, sont relativement peu connus et le mélange de fonctionnalités est inédit et le moins que l’on puisse dire c’est que tout se marie très bien !

Il a principalement été inspiré par le C++ (pointeurs intelligents), l’Erlang (système de tâches légères), le Haskell (le système de trait), les langages fonctionnels en général (filtrage par motif (pattern matching) et éléments de syntaxe), Python (quelques éléments de syntaxe), et sans doute d’autres.

C’est une véritable volonté de réutiliser ce que la riche histoire des langages de programmation leur avait laissé, et non de réinventer la roue une énième fois sans tirer des leçons du passé !

Mais Rust ne fait pas tout…

Ce qui suit est plus ou moins une traduction d’une partie de la FAQ présente sur Github. Certaines choses ne font pas partie des objectifs de Rust :

  • Utiliser des techniques innovantes : comme dit précédemment, Rust a très peu de nouvelles fonctionnalités, et au contraire se focalise sur l’exploitation de techniques connues, des écrits et des études sur le sujet, pour l’intégrer de façon cohérente au langage.

  • L’expressivité, le minimalisme ou l’élégance ne sont pas des buts en soi et ne sont donc pas plus importants que les autres buts du langage. En effet, le langage est performant, parallélisable et sûr en premier lieu.

  • Couvrir toutes les fonctionnalités bas niveau des « langages système » pour écrire un noyau de système d’exploitation. Bien que ce ne soit pas son but, nous verrons toutefois plus bas qu'il se prête plutôt bien à l’exercice.

  • Posséder toutes les fonctionnalités du C++ (la santé mentale des développeurs compte aussi !). Le langage fournit des fonctionnalités qui sont utiles dans la majorité des cas. On peut remarquer que c’est la même philosophie actuellement suivie dans Firefox.

  • Être 100% statique, 100% sûr ou 100% réflexif, et en règle générale, être trop dogmatique. Les compromis existent. Le langage a vocation à être pratique, et non « pur ».

  • Tourner sur n’importe quelle plateforme. Il devrait fonctionner sans trop de problèmes sur la plupart des plateformes matérielles et logicielles. Nous verrons plus bas qu’il est même possible de faire tourner des programmes Rust sur des plateformes matérielles un peu plus exotiques que la moyenne.

Montrez-moi le code !

Par rapport aux versions précédentes

Le langage commence à arriver à maturité, c’est pour cela qu’une bonne partie de la syntaxe reste identique par rapport aux versions précédentes (quand je dis les versions précédentes, je parle de deux ou trois versions en arrière). En effet, les évolutions ont surtout été de l’ordre des améliorations incrémentales de la syntaxe et de gros travaux dans la bibliothèque standard.

“Hello, world!” (what else?)

À quoi ressemble l’habituel et incontournable Hello world en Rust ?

fn main() {
    println("Hello, world");
}

Par convention, les sources Rust ont l’extension .rs, se compilent en faisant rustc hello.rs et produisent un fichier hello.

Vous pouvez aussi directement lancer la commande rust run hello.rs qui compilera et lancera l’exécutable.

Commentaires

En Rust, on utilise le même type de commentaires qu’en C (et beaucoup d’autres langages).

// Ceci est un commentaire sur une seule ligne
/* Ceci est un commentaire
sur plusieurs lignes */

Déclarations de constantes et de variables

Types de base, vec et str

Les déclarations se font avec le mot clef let. Dans la plupart des cas il n’est pas nécessaire de donner le type de la variable, car il est déduit à la compilation (inférence de type).

let a = 3; // a est de type int (entier)
let b = "Rust"; // b est de type str (chaine de caractères)
let c = [1, 2, 3]; // c est de type vec (tableau)

On peut aider un peu le compilateur en suffixant les valeurs :

let a = 1u; // a est de type uint (entier non-signé)
let b = 1i; // b est un int (entier signé)
let b32 = 1i32; // b est de type i32 (entier sur 32 bits)

let c = 1.0; // c est de type f64 (nombre flottant sur 64 bits)
// c’est le type double dans les autres langages
let d = 1f32; // d est de type f32
// c’est le type float dans les autres langages
let e = 1e-14f64; // e est de type f64 vaut 1×10^-14

let f = true; // f est de type bool (booléen)
let g = "Ceci n’est pas un texte !"; // g est de type str (chaine de caractères en UTF-8)
let h = [1u, 2, 3]; // pour plusieurs éléments de même type, un seul suffixe suffit

Le type peut être déterminé à partir de l’utilisation qui en est faite ensuite. En général, on n'utilise cette propriété que si l'on peut déterminer le type de la variable à partir du code juste en dessous (et pas 500 lignes plus bas).

let mut x = 1.0; // sans indication, le type déduit est f64
x = 1f32; // le compilateur devine que x est en fait un f32

let mut x = 1; // le type déduit est int
x = 2u; // finalement, c’est un uint

let mut x = ~[]; // vecteur de type inconnu (ne compile pas tout seul)
x = ["Ceci", "est", "un", "test"]; // vecteur de str

Sinon, on peut simplement donner le type explicitement :

let a: uint = 2; // annotation de type
let b: [f64, ..3] = [1f64, 2.0, 2.437]; // tableau de f64 de taille 3
// conversion (_cast_) d’un entier non-signé vers un entier signé
// méthode to_int() qui existe pour beaucoup de types de base
let c = fonction_qui_retourne_un_uint().to_int();
// À utiliser uniquement si on ne peut pas faire autrement
let d = fonction_qui_retourne_un_uint() as int;

Vous remarquerez assez vite que la conversion de type implicite n’existe pas en Rust, même entre les types numériques de base. Loin d’être un fardeau, c’est la garantie de trouver rapidement d’où vient son problème (et pas d’un bug qui provient d’une conversion implicite, bugs en général très difficiles à repérer).

Je viens de vous parler de vec mais sachez qu’il y a de nombreux autres conteneurs : des équivalents à map et set, une file à double fin (on peut ajouter et enlever à la fin ou au début, au choix), et une file ordonnée par une clé.

Mutabilité

En Rust, les données sont immuables par défaut. Le compilateur nous garantit que la valeur d’une variable ne pourra pas être modifiée pendant toute la durée de vie de cette variable. C’est une garantie bien plus forte que le const de C++, qui ne fait qu’interdire les modifications de la valeur au travers de cette variable const : il est toujours possible de modifier une structure de données déclarée const si on accède à son contenu depuis un pointeur qui n’est lui-même pas marqué const.

En Rust, le système de typage rend même le pointage non-constant d’une valeur constante impossible. Cette propriété du langage élimine toute une classe d’erreurs potentielles. Par exemple, cela supprime le problème d’invalidation d'itérateurs, qui est une source d’erreurs fréquentes en C++.

Si on veut pouvoir modifier sa valeur par la suite, il faut utiliser le mot-clé mut :

let mut total = 0;
total += 1;

En C++, il peut être plutôt difficile d’avoir un code qui respecte la const-correctness (concept cher aux développeurs C++ expérimentés qui consiste à marquer const tout ce qui peut l’être). Cela permet d’avoir un code plus sûr, plus facile à maintenir, et ça peut aider le compilateur à faire quelques optimisations.

Bref, vous le verrez également plus bas, le compilateur Rust assure que la mutabilité est correcte par défaut !

Variables statiques

Les variables statiques sont des variables globales définies directement dans un module à l’aide du mot clef static :

static toto: int = 42;

fn main() {
    println!("Ma variable statique: {}", toto);
}

Il est possible de définir une variable statique mutable. Ce faisant, il est possible de la modifier depuis n’importe quel point du programme. Étant donné que dans un environnement multitâche une variable statique est partagée entre les taches, son accès n’est pas synchronisé et donc potentiellement dangereux. C’est pour cela qu’il est nécessaire d’effectuer toute manipulation d’une variable statique dans un bloc unsafe :

static mut toto: int = 42;

fn main() {
    unsafe {
        toto = 0;
        println!("Ma variable statique: {}", toto);
    }
}

Notez qu’il est possible de définir des variables statiques mutable locales à chaque tâche. On appelle ça le Task-Local Storage, qui s’effectue grâce à une table associative attachée à chaque tâche. Pour plus de détails sur l’utilisation des TLS, ça se passe ici.

Guide de nommage

Au niveau du style, il est recommandé d’écrire les noms de fonctions, variables, et modules en minuscule en utilisant des tirets-bas (underscore) pour aider à la lisibilité, et d’utiliser du CamelCase pour les types. Les noms peuvent contenir des caractères UTF-8 tels que des accents, tant qu’ils ne provoquent pas d’ambigüités.

Vous pouvez aussi voir les conventions utilisées pour les dépôts concernant Rust.

Afficher du texte

Point de System.out.println(); ici ! Rust a des fonctions d’affichage de texte très bien conçues, qui font beaucoup penser à Python, et dont les noms font moins de 18 caractères !

print("Affichage simple");
println("Affichage simple + saut de ligne");

print!("Affichage avec syntaxe pour afficher des {}", "variables.");
// résultat : Affichage avec syntaxe pour afficher des variables.
println!("Affichage avec syntaxe pour afficher des {}", "variables + saut de ligne.");
// résultat : Affichage avec syntaxe pour afficher des variables + saut de ligne.

// On peut donner le type plutôt que de faire un ma_variable.to_str()
println!("La réponse est {:i}", 42);
println!("La réponse est {:s}", "42");

// On peut aussi… ne pas le donner !
println!("La réponse est {:?}", 42);
println!("La réponse est {:?}", "42");

// On peut donner un nom aux emplacements, très utile pour les traductions
println!("La réponse est {réponse:i}", réponse = 42);

Il y a encore bien d’autres choses, mais si vous souhaitez en savoir plus, je vous conseille de vous référer à la documentation.

Fonctions

Une fonction se déclare de la façon suivante :

fn ma_fonction(param1: Type, param2: Type) -> TypeDeRetour {
    // mon code
}

Les fonctions qui n’ont pas de type de retour sont généralement marquées avec le type de retour unit (aussi appelé « nil »). En Rust, les deux notations ci-dessous sont équivalentes :

fn ma_fonction(param1: Type, param2: Type) -> () {
    // mon code
}

fn ma_fonction(param1: Type, param2: Type) {
    // mon code
}

La syntaxe ressemble furieusement à du Python (avec annotations de type qui rappelons-le ne sont pas interprétées par Python).

Comme dans les langages fonctionnels, il est aussi possible d’omettre le mot clef return à la fin de la fonction en supprimant le point-virgule. Dans ce cas, le bloc de plus haut niveau (le plus imbriqué dans des accolades) de la fonction produit l’expression qui sert de valeur de retour à la fonction. Ainsi, les deux fonctions suivantes sont équivalentes :

fn mult(m: int, n: int) -> int {
    return m * n
}

fn mult(m: int, n: int) -> int {
    m * n
}

Enfin, il est possible d’écrire des fonctions imbriquées (nested functions, fonctions à l’intérieur d’autres fonctions), contrairement au C, C++ ou Java.

Les structures de contrôle

On retrouve la plupart des structures de contrôle habituelles. À noter que les conditions des structures de contrôle ne nécessitent pas de parenthèses et doivent être de type booléen (rappel : pas de conversions implicites). Le corps de la structure de contrôle doit obligatoirement être entre accolades.

Le classique if/else

if false {
    println("étrange");
} else if true {
    println("bien");
} else {
    println("ni vrai ni faux ?!");
}

On peut combiner la possibilité de ne pas utiliser de mot-clé return à la puissance du if/else (ça permet aussi avec match que l’on verra plus bas) afin d’éviter quelques lourdeurs :

// retourne la valeur absolue
fn abs(x: int) -> uint {
    if x > 0 { x }
    else { -x }
}

On peut aussi l’utiliser pour assigner des valeurs :

let est_majeur = true;
// Pas besoin d’opérateur ternaire
// En C++ ça donnerait:
// int x = (est_majeur)? "+18": "-18";
let x = if est_majeur { "+18" } else { "-18" };

match : switch puissance 1 000

match permet de faire du filtrage par motif (pattern matching) ainsi que déstructurer les structures de données (c’est-à-dire récupérer individuellement les valeurs).

match mon_nombre {
    0     => println("zéro"),
    1 | 2 => println("un ou deux"),
    3..10 => println("de 3 à 10"),
    _     => println("quelque chose d’autre")
}

Mais match est une des killer features de Rust, car un match qui ne traite pas toutes les possibilités ne compile pas.

// ne compile pas : on ne prend pas en compte les nombres négatifs et supérieur à 10
match mon_nombre {
    0     => println("zéro"),
    1 | 2 => println("un ou deux"),
    3..10 => println("de 3 à 10"),
}

// compile : tous les cas sont pris en compte grâce au joker _
match mon_nombre {
    0     => println("zéro"),
    1 | 2 => println("un ou deux"),
    3..10 => println("de 3 à 10"),
    _     => {} // ne fait rien
}

// compile : Rust a vérifié que l’on n'avait pas oublié de possibilités
match mon_nombre {
    x if x < 0 => println("strictement inférieur à zéro");
    0          => println("zéro"),
    1 | 2      => println("un ou deux"),
    3..10      => println("de 3 à 10"),
    x if x > 0 => {}
}

Pour ce qui est des performances, le filtrage par motif peut être assimilé à une union en C++, avec un tag (un nombre entier automatiquement généré par le compilateur, différent pour chaque entrée de l’union) permettant sélectionner la bonne entrée de l’union.

Boucle while

let mut nbr_gâteaux = 8;
while nbr_gâteaux > 0 {
    nbr_gâteaux -= 1;
}

Boucle for

// permet de boucler sur les éléments contenus dans un itérateur. (voir plus bas)
// l’itérateur que renvoie range va de 0 à 9
// _ est un joker : aucune variable ne prend les valeurs « renvoyées » par range
for _ in range(0, 10) {
    println("blam!");
}

// on peut bien sûr parcourir des vecteurs
let mon_vecteur = [-1, 0, 1];
// la méthode iter() permet de récupérer un itérateur
// invert permet d’inverser le sens de l’itérateur
for i in mon_vecteur.iter().invert() {
    println(i.to_str());
}
// Cela affichera donc :
// 1
// 0
// -1

Itérateurs

Un petit point sur les itérateurs tout de même. On peut obtenir de n’importe quel conteneur un itérateur, mais on pourrait imaginer un itérateur sur n’importe quelle suite mathématique.

De plus, les itérateurs ont certaines méthodes bien pratiques…

let xs = [1, 9, 2, 3, 14, 12]; // un vec quelconque
// La méthode fold permet d’accumuler les valeurs d’un itérateur
let result = xs.iter().fold(0, |accumulator, item| accumulator - *item);
// result vaut -41

Pour plus d’infos, c’est par ici.

Boucle loop, ça c’est nouveau

loop permet de faire des boucles infinies ! En fait, c’est l’équivalent de while true, il permet de remplacer do {} while(); (si on met un if qui contient un break juste avant la fin de la boucle) tout en étant plus flexible.

let mut x = 5u;
loop {
    x += x - 3;
    if x % 5 == 0 { break; }
    println(x.to_str());
}

Les structures de données

struct

Compatible avec les struct en C, c’est une structure de données qui permet de regrouper plusieurs variables.

struct Magicien {
    pv: uint,
    pm: uint
}

let mut mon_magicien = Magicien { pv: 2, pm: 3 };
mon_magicien.pv = 3; // pv de mon_magicien vaut désormais 3

// On peut aussi créer des `struct` vides (pour faire des tests par exemple)
struct StructVide;
let ma_struct_vide = StructVide;

On peut implémenter des méthodes sur des struct, ce qui nous donne plus ou moins une classe.

impl Magicien {
    // par convention, `new` crée, initialise et renvoie une structure
    // on met `mut` devant le nom du paramètre pour pouvoir le modifier
    fn new(mut pv_initiaux: uint, mut pm_initiaux: uint) -> Magicien {
        // on vérifie qu’on ne viole pas les invariants de classe
        if pv_initiaux == 0 || pv_initiaux > 10 {
            pv_initiaux = 2;
        }
        if pm_initiaux == 0 || pm_initiaux > 20 {
            pm_initiaux = 3;
        }

        // finalement on crée la structure que l’on va renvoyer
        Magicien {
            pv: pv_initiaux,
            pm: pm_initiaux
        }
    }

    // si notre magicien perd de la vie
    fn perd_vie(&mut self, vie_perdu: uint) {
        if vie_perdu > self.pv { self.pv = 0; }
        else { self.pv -= vie_perdu; }
    }

    // on veut pouvoir récupérer ses pv pour l’affichage ou le debug
    fn get_pv(&self) -> uint {
        self.pv
    }

// La méthode `new` ne prend pas `&self` en paramètre.
// C’est l’équivalent d’une méthode statique.
// On l’appelle donc comme ceci :
let mut mon_magicien = Magicien::new(2, 4);

// La méthode `perd_vie()` prend mut `&self` en paramètre.
// Cela signifie qu’on désigne une instance de la structure 
// et que l’on souhaite pouvoir la modifier.
// On l’appelle donc comme ceci :
mon_magicien.perd_vie(2);

// La méthode `get_pv(`) prend `&self` en paramètre.
// Cela signifie qu’on opère sur une instance de la structure
// mais cette fois, on ne souhaite pas la modifier.
println(mon_magicien.get_pv().to_str());

On remarquera que certaines méthodes prennent un self en premier paramètre. Il s’agit d’un identifiant représentant la structure courante (un peu comme le pointeur this dans la plupart des langages orientés objet. Sauf qu’ici on a plus de libertés sur la sémantique de son passage en argument : par référence avec &self, par mouvement avec self, etc). Par exemple dans mon_magicien.perd_vie(2), on aura self égal à mon_magicien. Une méthode sans paramètre self est une méthode statique.

Remarque : si on crée une instance de structure sans passer par new, il est quand même possible d’utiliser les méthodes définies dans le bloc impl. En fait, new n’est rien d’autre qu’une méthode statique comme les autres qu’on aurait très bien pu appeler create, bob voire choux_fleur. Ça n’a rien à voir avec les constructeurs ou la surcharge de l’opérateur d’allocation new en C++.

enum

Dans son utilisation la plus simple, une enum Rust est comparable à une enum de C. Il est également possible d’implémenter des méthodes dessus (un peu comme en Java).

enum Coup {
    Pierre, Feuille, Ciseaux
}

impl Coup {
    fn to_str(&self) -> ~str { // on renvoie une chaine de caractère
        match *self { // on utilise l’étoile pour accéder à la valeur
            Pierre => ~"pierre",
            Feuille => ~"feuille",
            Ciseaux => ~"ciseaux"
        }
    }
}

Chaque variante d’un enum peut avoir une valeur numérique… comme en C.

enum Coleur {
    Rouge = 0xff0000,
    Vert = 0x00ff00,
    Bleu = 0x0000ff
}

Enfin, un enum peut faire des choses beaucoup plus puissantes, car elle permet en réalité de définir des types algébriques.

struct Point { x: int, y: uint }

// l’enum sert à choisir entre deux structures de données
// qui ont une représentation mémoire différente
enum Forme {
    Cercle(Point, f64),
    Rectangle(Point, Point)
}

// On peut aussi choisir entre plusieurs `struct`s.
enum Forme {
    Cercle { centre: Point, rayon: f64 },
    Rectangle { haut_gauche: Point, bas_droit: Point }
}

// Dans ce cas-là, on déconstruit en utilisant les accolades
fn aire(forme: Forme) -> f64 { // calcule l’aire de la figure
    match(forme) {
        Cercle { rayon: rayon, _ } => f64::consts::pi * square(rayon),
        Rectangle { haut_gauche: haut_gauche, bas_droite: bas_droite } => {
            (bas_droite.x - haut_gauche.x) * (haut_gauche.y - bas_droite.y)
        }
    }
}

Tuples

Des tuples, comme en Python.

let position1 = (2, 4.0); // laisse le compilateur deviner les types
let position2: (int, f32) = (2, 4.0); // donne le type explicitement

// tuple vide, renvoyé par les fonctions censées ne rien renvoyer (on vous a menti !)
let tuple1 = ();
// tuple d’une seule valeur, pas très utile
let tuple2 = (4);
// Préférez une `struct` si vous avez beaucoup de valeurs
let tuple3 = (23, 8, -1, 78, -4);

// On peut extraire des valeurs des tuples
let tuple = (5, -6, 4);
let (premier, _) = tuple;
// premier vaut 5, le _ indique qu’on ne se préoccupe pas de la seconde valeur du tuple

match position1 {
    (x, y) if x > 0 && y > 0.0 => {}
    (_, y) if y < 0.0          => {}
    (_, _)                     => {}
}

Là aussi, match est capable de savoir si toutes les possibilités ont été épuisées.

Tuple struct

Les tuple structs sont tout simplement des tuples auxquels on donne un nom.

struct tuple_struct(int, int, f32);
let mon_tuple = tuple_struct(1, 4, 30.0);
match mon_tuple {
    tuple_struct(a, b, c) => println!("a: {:i}, b: {:i}, c: {:f}", a, b, c)
}

Le tuple struct à un seul champ est un cas particulier très utile pour définir un nouveau type (appelé comme cela d’après la fonctionnalité d’Haskell newtype). Le compilateur conservera la même représentation mémoire pour le type contenu dans le tuple, et le tuple lui-même. En revanche, il s’agit d’un tout nouveau type : on peut lui ajouter de nouvelles méthodes alors que celles du type contenu ne sont accessibles que par déconstruction du tuple grâce à l’opérateur de déréférencement *.

Il ne faut pas le confondre avec type IdBidule = int; qui crée simplement un alias de type, comme typedef en C ou using en C++11.

// On crée et on déclare de la même façon
struct IdBidule(int);
let mon_id_bidule: IdBidule = IdBidule(10);
// cette syntaxe est valide pour le cas particulier des nouveaux types.
let id_int: int = *mon_id_bidule; // déconstruit le tuple-struct pour en extraire l’entier.

C’est très utile pour différencier des données de même type mais qui doivent être utilisées différemment.

struct Pouces(int);
struct Centimètres(int);

Type Option

Autre killer feature du Rust, le type Option permet de gérer les cas d’erreurs où on utiliserait des pointeurs nuls ou des exceptions dans les autres langages ! Ils sont remplacés par Option, type que l’on peut déstructurer :

// fonction_dangereuse_en_C renvoie un Option<int>
nbr = match fonction_dangereuse_en_C() {
    // x est du type int, on peut le manipuler entre les accolades
    Some(x) => { x } // si ça a réussi, nbr = x
    None => { 0 } // sinon, on met une valeur par défaut (nbr = 0)
}

Le compilateur optimise automatiquement certains types Option comme Option<~int> afin qu’ils soient réellement représentés en mémoire par des pointeurs nuls (et non plus une union taguée).

Nous n’aborderons pas ici les autres moyens de gérer les erreurs en Rust, qui peuvent mieux convenir dans certains cas mais qui sont moins utilisés et plus complexes.

Récupérer des informations depuis l’entrée standard

Il y a des opérations basiques :

use std::io; // pour utiliser le module d’entrées/sorties
// à terme, on utilisera std::rt::io (qui n’est pas encore fini)

// io::stdin().read_line() renvoie l’entrée utilisateur (chaine de caractères)
let arg = io.stdin().read_line();

Mais dans pas mal de cas il faut passer par le type Option que l’on vient de voir :

// from_str::<int> convertit la chaine en entier et la renvoie dans un Option<int>
let arg = from_str::<int>(io::stdin().read_line());

// On peut aussi le faire de cette façon :
let arg: Option<int> = FromStr::from_str(io::stdin().read_line());
// Il faut ensuite utiliser un match pour récupérer le résultat

Exemples

Voici quelques exemples classiques (et surtout qui servent à quelque chose) reprenant la plupart des concepts vu ci-dessus.

Fizz Buzz

Voici un exemple de la puissance du Rust:

fn main() {
    for i in range(0u, 101) {
        match (i % 3 == 0, i % 5 == 0) {
            (true, true)   => println("Fizz Buzz"),
            (true, false)  => println("Fizz"),
            (false, true)  => println("Buzz"),
            (false, false) => println(i.to_str())
        }
    }
}

Vous voulez plus de Fizz Buzz ?

Récupérer une saisie utilisateur

Ici on veut récupérer la valeur absolue du nombre que l’utilisateur a entré. Ça va vous permettre de jeter un œil aux fonctions utilisées pour les entrées en Rust. C’est surtout l’occasion de voir comment régler proprement un problème qu’on s’est forcément posé une fois quand on était débutant.

// Pour pouvoir utiliser certaines parties de la bibliothèque standard
use std::io;
use std::num;

fn main() {
    let mut nbr;
    println("Entrez un nombre s’il vous plait : ");

    loop {
        let arg = from_str::<int>(io::stdin().read_line());
        match arg {
            None => { println("Ce n’est pas un nombre."); },
            Some(x) => {
                nbr = num::abs(x).to_uint(); // num::abs() renvoie un entier
                break; // on sort de la boucle
            }
        }
        // sinon on a un message d’erreur
        println("Veuillez entrer une valeur correcte : ");
    }
} 

Clôture (closure)

Les clôtures, ce sont des fonctions qui peuvent capturer des variables de la portée (scope) en dessous de la leur, c’est-à-dire qu’elles peuvent accéder aux variables déclarées au même niveau que la clôture. De plus, on peut passer des clôtures à une autre fonction, un peu comme une variable.

fn appeler_clôture_avec_dix(b: &fn(int)) { b(10); }

let var_capturée = 20;
let clôture = |arg| println!("var_capturée={}, arg={}", var_capturée, arg);

appeler_clôture_avec_dix(clôture);

Des fois, il est nécessaire d’indiquer le type :

// fonction carré, renvoie le carré du nombre en paramètre
let carré = |x: int| -> uint { (x * x) as uint };

On peut aussi faire des clôtures anonymes :

let mut max = 0;
[1, 2, 3].map(|x| if *x > max { max = *x });

Parallélisation

do spawn

Pour lancer une nouvelle tâche, il suffit d’écrire do spawn, puis de mettre tout ce qui sera exécuter dans la nouvelle tâche entre accolades.

// Lancement un traitement dans une autre tâche
do spawn {
    // Gros traitement
}

// Lancer pleins de trucs en parallèle
for i in range(1, 100) { // Pour les entiers de 1 à 99
    do spawn { // On crée une tâche qui affiche l’entier à l’écran
        println(i.to_str());
    }
}

Canal

Pour communiquer entre processus en C, on utilise les tubes (pipes). En Rust, on utilise les canaux pour communiquer entre les tâches.

// on crée le canal de communication
let (port, chan): (Port<int>, Chan<int>) = stream();

do spawn {
    let result = some_expensive_computation();
    chan.send(result); // on envoie le résultat
    // notez qu’on ne peut plus utiliser chan dans la tâche principale
    // car elle a été « capturée » par la tâche secondaire
}

some_other_expensive_computation(); // calcul dans la tâche principale
let result = port.recv(); // on reçoit le résultat de la tâche secondaire

Un exemple de la « capture » des variables par la tâche

let (port, chan) = stream(); // on n’a pas précisé le type, en effet il peut être déduit

do spawn {
    chan.send(some_expensive_computation()); // on envoie le résultat du calcul
}

// Erreur, car le bloc `do spawn` précédent possède la variable `chan`
do spawn {
    chan.send(some_expensive_computation());
}

Canal partagé

Pour lancer plein de tâches, mais tout récupérer au même endroit :

let (port, chan) = stream();
let chan = SharedChan::new(chan); // chan devient un canal partagé

for init_val in range(0u, 3) {
    // Create a new channel handle to distribute to the child task
    let child_chan = chan.clone();
    do spawn {
        child_chan.send(some_expensive_computation(init_val));
    }
}

let result = port.recv() + port.recv() + port.recv();

Notez qu’on peut utiliser les itérateurs pour changer la dernière ligne et rendre notre code beaucoup plus flexible…

// Cela fonctionne pour n’importe quel nombre de tâches secondaires
let result = ports.iter().fold(0, |accum, port| accum + port.recv() );

Retour vers le futur

Il est possible de faire un calcul en arrière-plan pour le récupérer quand on en a besoin grâce à future.

fn fib(n: uint) -> uint { // calcule le nombre de Fibonacci de n
    // long calcul qui renvoie un uint
}

let mut fib_différé = extra::future::Future::spawn (|| fib(50) );
faire_un_sandwich();
println!("fib(50) = {:?}", fib_différé.get())

Les boites et les pointeurs

Jusqu’à maintenant, on créait des variables et des structures de données sur la pile. Cela signifie que si on passe cette variable à une fonction par exemple, on effectue forcément une copie. Pour de grosses structures ou des objets mutables, il peut être intéressant d’avoir une seule copie de la donnée sur la pile ou sur le tas et de la référencer par un pointeur.

En Rust, on a les pointeurs qui se contentent de pointer sur une valeur (comme son nom l’indique), et les boites (correspondant aux pointeurs intelligents du C++) qui vont avoir une influence sur la durée de vie de la valeur (si une valeur n’a plus de boite qui la référence, elle est supprimée). La différence n’est pas essentielle, mais ça permet de mieux comprendre le fonctionnement de Rust.

Pointeur unique (Owned pointer)

C’est une boite qui correspond à peu près à unique_ptr<T> en C++. Concrètement, la boite « possède » la valeur sur laquelle il pointe, et si on décide d’utiliser une autre boite ou un autre pointeur sur cette variable, on ne pourra plus utiliser l’ancienne. On appelle cela la sémantique de mouvement. Quand le pointeur est détruit, la valeur sur laquelle il pointe sera détruite (la durée de vie de l’objet pointé est celle de sa boite unique).

{
    // on déclare un pointeur unique avec un ~ devant la valeur
    let x = ~"Test";
} // x et la chaine de caractères sur laquelle il pointe sont détruites

{
    let x = ~"Test";
    let y = x; // on passe la propriété de la chaine de caractères à y
    // erreur de compilation, c’est y qui possède la chaine de caractères
    x = "Plus test";
}
// x est supprimé
// y est supprimé ainsi que la chaine de caractères qu’elle possède

{
    let mut x;
    {
        let y = ~"Test";
        x = y; // on ne peut plus utiliser y
    }
    // y est supprimé
    // mais x possède la chaine de caractères qui n’est donc pas détruite
} // x et la chaine de caractères sur laquelle il pointe sont supprimés

Boite partagée (Managed box)

C’est une boite qui correspond à peu près au shared_ptr<T> en C++ et au système utilisé dans Python, Java, Ruby… Plusieurs boites différentes peuvent référencer une même valeur, et lorsque la dernière référence est détruite, un ramasse-miette s’occupe de libérer la mémoire.

{
    let mut x;

    {
        let y = @"Un str dans une boite partagée"; 
        let x = y; // x et y pointent vers la même chaine de caractères
    } // y est supprimé
}
// x est supprimé ainsi que la chaine de caractères
// en effet, x et y ont tous les deux été supprimés

Rust fait très attention à la mutabilité…

let w = @0;
let x = @mut 1;
let mut y = @2;
let mut z = @mut 3;

z = y; // impossible, la valeur de z est mutable alors que celle de y ne l’est pas
y = z; // bien entendu, ça ne fonctionne pas non plus dans l’autre sens

Deux particularités devraient cependant retenir votre attention. D’une part on choisit ce qui sera géré par le ramasse-miettes, ce qui fait qu’il ne gère que ce qui est nécessaire (il est donc plus rapide). D’autre part, il n’y a pas un ramasse-miettes global, mais un ramasse-miettes par tâche qui le nécessite (possible car il n’y a pas de mémoire partagée), ce qui signifie qu’un programme multitâche (multithreadé) ne sera jamais complètement arrêté.

C’est une fonctionnalité presque indispensable au sein d’un moteur de rendu comme Servo. Pour le moment, c’est un simple compteur de références qui ne gère pas correctement les références circulaires, mais dans le futur, un vrai ramasse-miettes sera implémenté.

Il est intéressant de noter que l'API standard de Rust n’utilise que très rarement des boites partagées. En fait, il est relativement courant qu’un programme Rust n’utilise que des valeurs sur la pile et des pointeurs uniques, ce qui au final revient à ne pas utiliser de ramasse-miettes. Le fait de pouvoir se passer totalement de ramasse-miettes, et ceci sans avoir à trop restreindre l’utilisation de l'API standard, est un point fort pour développer dans certains domaines (jeux, temps réel).

Pointeur emprunté (Borrowed pointer)

Correspond à la référence en C++. C’est simplement un pointeur sur la mémoire appartenant à une autre boite ou pointeur. Il est surtout utilisé pour les fonctions, on peut alors lui passer en paramètre n’importe quelle valeur, boite ou pointeur :

// Le & devant le type signifie qu’on accepte n’importe quelle valeur, boite ou pointeur
fn test(mon_vecteur: &[uint]) {
    // mon code
}

// un vecteur alloué sur la pile, et deux boites (allouées sur le tas)
let x = [1, 2, 3];
let y = ~[1, 2, 3];
let z = @[1, 2, 3];

// Grâce au pointeur emprunté, les trois appels ci-dessous sont valides
test(x);
test(y);
test(z);

Ça permet aussi de « geler » temporairement une variable :

let mut x = 5; // je peux modifier x
{
    let y = &x;
    let x = 6; // Erreur, x ne peut être utilisé tant que y existe
    let y = 7; // Erreur, y n’est pas mutable
}
// Je peux à nouveau utiliser x

// Le cas de la boite partagée est intéressant…
let mut x = @mut 5;
{
    // notez l’étoile, on cherche l’adresse de la valeur et non celle de la boite
    let y = &*x;
    // si on essaie de modifier x ou y, le programme va lancer un échec
    // http://static.rust-lang.org/doc/master/tutorial-conditions.html#failure
    // il va « planter » proprement. En effet, l’état de gel des boites partagées
    // est vérifié à l’exécution.
}

Pointeur brut (Raw pointer)

Quand nous vous avions dit tout au début que Rust était un langage totalement sûr, nous vous avions menti ! En effet, il est possible d’écrire du code non-sûr mais seulement dans un bloc ou une fonction marquée unsafe. Ils sont principalement utilisés pour FFI (pour appeler des fonctions d’un code écrit dans un autre langage, voir plus bas) ou, rarement, pour des opérations qui nécessitent plus de performance.

Le mot-clé unsafe (ce qui signifie « non-sûr ») permet en effet d’avoir accès à un pointeur non sécurisé (risque de fuite mémoire, multiple désallocation, valeur déréférencée, désallocation du pointeur pas claire), le type de pointeur utilisé en C (*). Le déréférencement est non sécurisé pour ce type.

Ce genre de pointeur est aussi utile pour définir ses propres types de pointeurs intelligents. Par exemple la bibliothèque étendue extra fournie avec le compilateur contient deux autres pointeurs intelligents : Rc et Arc, respectivement pour le comptage de référence et le partage de données entre plusieurs taches s’exécutant en parallèle.

Si vous souhaitez en savoir plus sur le code non-sûr, consultez le manuel.

Plus de détails sur le fonctionnement des pointeurs

Déréférencement de boites et de pointeurs

Si on crée une boite ou un pointeur pour une valeur, on veut pouvoir modifier son contenu. Pour y accéder, il y a deux manières :

let mut x = ~10;
x = ~10; // on assigne à nouveau une valeur dans une boite
x = 10; // invalide
*x = 10; // l’étoile permet d’accéder à la valeur comme en C

Cela fonctionne de la même façon pour les struct et les méthodes.

struct Test { x: int, y: int }
impl Test {
    pub fn test() { print("Un petit test."); }
}

let mon_test = ~Test { x: 10, y: 5 }
(*mon_test).x = 4;
(*mon_test).test();

Mais rassurez-vous, Rust fait du déréférencement automatique ! Cela signifie que vous n’avez pas à utiliser l’étoile lorsque vous voulez accéder à une valeur ou une méthode d’une struct. Ainsi, le code suivant est parfaitement valide :

mon_test.x = 4;
mon_test.test();

Les durées de vie

Les durées de vie sont peut-être la fonctionnalité inédite du Rust. Ils permettent de créer des pointeurs sur à peu près n’importe quoi (y compris sur la pile), tout en garantissant qu’ils ne soient jamais invalides.

En fait, tous les pointeurs empruntés ont une durée de vie. La plupart du temps, le compilateur les déduit automatiquement.

struct UneGrosseStructure {
    donnée_énorme_à_ne_pas_copier: f64 // imaginez qu’à la place de f64 on ait vraiment une donnée de plusieurs Mo.
}

fn main() {
    let donnée = UneGrosseStructure { donnée_énorme_à_ne_pas_copier: 42.0 };
    pointeur_emprunté_vers_la_donnée = &donnée.donnée_énorme_à_ne_pas_copier;

    // À partir de maintenant, le compilateur utilise les durées de vie
    // pour s’assurer que le pointeur emprunté ne survive pas après
    // la destruction de la donnée.

    // compile car la structure donnée existe encore !
    println(pointeur_emprunté_vers_la_donnée.to_str());
}

En revanche il est des situations où le compilateur ne peut inférer correctement les durées de vie. Cela arrive systématiquement lorsque l’on essaie de retourner un pointeur emprunté vers une donnée interne à une structure.

struct UneGrosseStructure {
    donnée_énorme_à_ne_pas_copier: f64
    // imaginez qu’à la place de f64 on ait vraiment une donnée de plusieurs Mo.
}

impl UneGrosseStructure {
    fn get_data_ref(&self) -> &f64 { // ceci ne compile pas
       &self.donnée_énorme_à_ne_pas_copier
    }
}

Ceci ne peut pas compiler étant donné que rien n’indique à l’appelant de la méthode get_data_ref que le pointeur qu’il retourne pointe vers l’intérieur de la structure. En effet, lorsqu’on appelle get_data_ref de l’extérieur, on a besoin de savoir que le &f64 retourné n’est valide que tant que &self est lui-même valide. Cette synchronisation de validité de pointeurs se fait par le biais d’une annotation de durée de vie explicite :

struct UneGrosseStructure {
    donnée_énorme_à_ne_pas_copier: f64
    // imaginez qu’à la place de f64 on ait vraiment une donnée de plusieurs Mo.
}

impl UneGrosseStructure {
    fn get_data_ref<'a>(&'a self) -> &'a f64 { // ceci compile! Le pointeur retourné et self ont le même tag: 'a.
       &'a self.donnée_énorme_à_ne_pas_copier
    }
}

Vous pouvez voir le 'a (annotation de durée de vie) comme un tag de pointeur qui va dire que « tous les pointeurs tagués par un 'a doivent vivre au plus aussi longtemps que le self tagué avec un 'a. ». Il sera ainsi impossible à la structure dont on a pris un pointeur interne d’être détruite avant que le pointeur interne lui-même ait été détruit.

Voici un autre exemple, utilisant la même structure que précédemment, de ce que l’on aurait pu faire (à tort) sans la notion de durée de vie. Si on avait le droit d’écrire fn get_data_ref(&self) -> &f64, on aurait été capable d’écrire cela :

/*
 * Ceci est ce que l’on aurait pu faire si la notion de durée de vie de pointeur n’existait pas.
 */
fn créer_un_pointeur_invalide() -> &f64 {
    // on crée la donnée
    let donnée = UneGrosseStructure { donnée_énorme_à_ne_pas_copier: 42.0 };

    // on prend une référence
    let référence = donnée.get_data_ref();

    // on fait plein de trucs fun avec
    println(référence.to_str());

    // et… on la retourne !
    référence
}

fn main() {
   let pointeur_invalide = créer_un_pointeur_invalide();

   println(pointeur_invalide.to_str());
}

Si ceci était autorisé, il est évident que le pointeur_invalide est invalide étant donné qu’il pointe sur la pile allouée pour l’appel de fonction créer_un_pointeur_invalide.

Voyons comment, en ayant défini fn get_data_ref<'a>(&'a self) -> &'a f64, les durées de vie nous aident ici :

/*
 * Ceci est du code Rust qui ne compilera pas, grâce aux durées de vie
 */
fn créer_un_pointeur_invalide() -> &f64 {
    // on crée la donnée
    let donnée = UneGrosseStructure { donnée_énorme_à_ne_pas_copier: 42.0 };

    // on prend une référence
    // `donnée` et `référence` sont synchronisés par 'a.
    let référence = donnée.get_data_ref()
    // 'a est toujours vivante

    // on fait plein de trucs marrants avec
    // 'a est toujours valide.
    println(référence.to_str());
    // 'a est toujours valide.

    // et … on ne peut pas la retourner ! (erreur de compilation)
    // 'a est toujours valide
    référence
    // 'a n’est _plus_ valide à la sortie de la fonction !
}

Ici, le 'a permet de suivre pendant combien de temps donnée est valide. On ne peut pas retourner le pointeur puisque référence est de type &'a f64 alors que le type de retour de la fonction est &f64. On voit bien que les durées de vie ne sont pas les mêmes.

La sémantique de mouvement

Il faut noter qu’en Rust, la méthode de passage d’argument par défaut n’est ni par copie, ni par référence. Il s’agit d’un passage par déplacement, c’est-à-dire en utilisant la sémantique de mouvement. C’est un peu comme si on appelait la fonction C++ std::move sur chacun des paramètres avant l’appel de fonction.

Cette sémantique de mouvement s’applique pour les pointeurs uniques (rappel : indiqué par le préfixe ~), les structures contenant de tels pointeurs, et les types génériques (cf. la section suivante). Tous les autres types sont copiés implicitement (les pointeurs @ et @mut effectuent une copie légère).

En effet, comme on sait que les pointeurs uniques ne peuvent pas être partagés (un seul pointeur dessus à la fois), on peut effectuer l’opération de déplacement sans risque. L’avantage principal de ce comportement est de permettre au programmeur de toujours savoir exactement à quel moment une copie nécessitant une allocation est réalisée.

struct Toto {
    données: ~[int], // grosse quantité de données qu’on veut éviter de copier
    autre_chose: int
}

fn extraire_données(t: Toto) -> ~[int] {
    t.données
}

fn main() {
    let toto    = Toto { données: ~[10, 20, 40, 80], autre_chose: 23 }; // toto avec un gros vecteur
    let données = extraire_données(toto); // pas de copie ici !

    // À partir d’ici, toto a été déplacé et n’est plus utilisable !

    println(données.to_str()); // on affiche les données
}

Dans cet exemple, le vecteur données n’est jamais copié ! Il est simplement déplacé hors de la variable toto. Ceci rend toto inutilisable après l’appel à extraire_données (et c’est vérifié par le compilateur). Si on souhaite éviter cette sémantique de mouvement, il est nécessaire de passer Toto en utilisant un pointeur, et de copier explicitement les données avec la méthode clone :

struct Toto {
    données:     ~[int], // grosse quantité de données qu’on veut éviter de copier
    autre_chose: int
}

fn extraire_données(t: &Toto) -> ~[int] {
    t.données.clone() // copie explicite
}

fn main() {
    let toto    = Toto { données: ~[ 10, 20, 40, 80 ], autre_chose: 42 }; // toto avec un gros vecteur
    let données = extraire_données(&toto); // on fait copie ici !

    // À partir d’ici, toto est toujours utilisable !

    println(données.to_str()); // on affiche les données 
}

Interactions avec les autres langages

Appeler du code d’un autre langage

Il est possible d’utiliser toutes les fonctions de la libc directement depuis Rust.

De plus, il est très facile d’appeler du code C grâce à FFI, il suffit de faire une fonction pour « envelopper » l’appel, mettre éventuellement du code non-sûr (souvent pour les pointeurs), s’occuper des correspondances entre les types de C et les types de Rust et deux-trois petits trucs supplémentaires.

Par exemple, si on veut faire la fork bomb la plus courte en Rust :

#[fixed_stack_segment]
fn main() {
    loop { // boucle infinie
        unsafe { std::libc::fork(); } // le fork, merci la libc
    }
}

Vous remarquerez que c’est quand même compliqué de faire des bêtises en Rust !

Il y a des discussions en cours pour amener le support C++ au même niveau que le C en s’inspirant du D, mais pour le moment, aucun autre langage que le C n’est supporté. Il faut donc créer un binding (c’est-à-dire refaire l’opération décrite ci-dessus pour toute la bibliothèque) en C pour ce code puis faire un binding Rust qui appelle ces fonctions C. C’est le même fonctionnement assez similaires aux autres langages de programmation.

Appeler du code Rust depuis un autre langage

On peut appeler du code Rust depuis n’importe quel langage qui peut appeler du code C en déclarant ses fonctions extern "C" fn foo(…) {}.

Néanmoins, vous ne pouvez utiliser qu’un sous-ensemble de Rust. Les tâches, les échecs et les pointeurs partagées notamment ne fonctionneront pas, car le runtime n’a pas été initialisé.

De plus, les (rares) parties de la bibliothèque standard qui utilisent les pointeurs partagés ne fonctionneront pas, notamment la partie io. Dans le futur, Rust sera plus facilement utilisable depuis un autre langage, mais cela nécessite du travail.

Si cela vous intéresse, voilà comment utiliser du Rust à partir de Ruby (certaines informations sont obsolètes).

La généricité

La généricité est la capacité d’écrire du code une seule fois et qui fonctionne pour de plusieurs types de données différents.

Les traits

Un trait est un outil permettant de contraindre un autre type à fournir un certain nombre de méthodes. C’est l’équivalent des interfaces de Java, des typeclasses d’Haskell.

En C++, on pensera plutôt aux classes abstraites et de ce qu’aurait pu être la notion de concept en C++1 (qui n’a pas été retenue par le comité de standardisation). Il y a également le système de templates qui n’a pas vraiment d’équivalent Rust (mais rassurez-vous, on se débrouille sans !).

Supposons que vous faites un moteur de rendu. Vous voudrez par exemple avoir des structures désignant quelque chose qui peut être dessiné. En d’autres termes, il est nécessaire d’imposer à un type d’avoir une méthode draw (dessiner en français). Pour cela, on va dans un premier temps créer un trait.

trait Draw {
    fn draw(&self);
}

Ensuite, tous les objets qui devraient pouvoir être dessinés ont simplement à implémenter le trait Draw :

struct A {
    data_a: int
}

struct B {
    data_b: f64
}

impl Draw for A {
    fn draw(&self) {
        println(self.data_a.to_str());
    }
}

impl Draw for B {
    fn draw(&self) {
        println(self.data_b.to_str());
    }
}

Ensuite il devient possible d’écrire des fonctions génériques qui fonctionneront aussi bien pour A que pour B et toute autre structure implémentant Draw.

fn draw_object<T: Draw>(object: &T) {
    println("Je vais dessiner sur la console un objet qui implémente Draw !");
    object.draw();
    println("Ça y est, j’ai fini ! :p");
}

fn main() {
    let a = A { data_a: 42 };
    let b = B { data_b: 42.0 };
    draw_object(a);    // OK, A implémente Draw.
    draw_object(b);    // OK, B implémente Draw.
    draw_object(42.0); // erreur de compilation: f64 n’implémente _pas_ Draw !
}

Notez-le <T: Draw>. Cela signifie que la fonction draw_object accepte n’importe quel type que l’on nomme abstraitement T, et que ce type doit implémenter le trait Draw.

Pour manipuler des éléments du type Draw lui-même, il est possible d’utiliser l’opérateur as pour que le compilateur considère la structure implémentant le trait Draw comme étant de type ~Draw. On appelle les instances du type ~Draw (ou @Draw et &Draw) des trait-object (des traits vus comme des objets).

let liste_de_trucs_qui_peuvent_être_dessinés = ~[~Draw]; // une liste hétérogène !
liste_de_trucs_qui_peuvent_être_dessinés.push(~A { data_a: 42 } as ~Draw);
liste_de_trucs_qui_peuvent_être_dessinés.push(~B { data_b: 42.0 } as ~Draw);
Les détails compliqués

Le comportement du compilateur vis-à-vis des fonctions (et structures) génériques est similaire au C++ : les fonctions polymorphiques (génériques) sont rendues monomorphiques pour chaque type d’argument avec lequel il est appelé. Pour faire simple, c’est exactement comme si le compilateur générait automatiquement les fonctions non-génériques :

fn draw_object(object: &A) { // Généré par le compilateur lorsqu’il voit que draw_object(a) est appelé.
   // ...
}

fn draw_object(object: &B) { // Généré par le compilateur lorsqu’il voit que draw_object(b) est appelé.
   // ...
}

Cela est très important pour les performances étant donné que la résolution des fonctions est réalisée au moment de la compilation et non lors de l’exécution. C’est pour cela que les traits sont très différents des interfaces en Java, ou des classes abstraites en C++. Pour faire simple : les traits en Rust font l’objet de dispatch statique de fonction, alors que les interfaces en Java font l’objet de dispatch dynamique.

Les traits sont l’objet de dispatch statique de fonction. Le dispatch dynamique, comme les interfaces de Java, (utilisées pour les listes hétérogènes dans le dernier exemple de la partie précédente) est assuré grâce au mécanisme de trait-object.

Pour résumer, on peut avoir du dispatch statique en utilisant une contrainte de type <T: Draw>, et de dispatch dynamique en utilisant un trait-objet ~Draw. Il s’agit d’un pont entre statique et dynamique très élégant en Rust que l’on trouve dans peu de langages.

Bien entendu, ceci n’est qu’un aperçu de la généricité en Rust, il est possible de faire beaucoup de choses puissantes comme de l’héritage de trait, les méthodes par défaut, les structures génériques, etc.

Les catégories (Kind)

Les catégories sont des traits un peu particuliers étant donné qu’ils ne pourraient pas être déclarés par un utilisateur du langage : ils nécessitent un support particulier de compilateur. Ceux-ci permettent principalement de contraindre la durée de vie des types ou de ce qu’ils contiennent (dans le cas où ils contiendraient des pointeurs empruntés).

Il n’est pas forcément nécessaire d’entrer dans les détails des catégories ici, il faut juste réaliser qu’elles permettent quelques actes de magie très puissants. Notamment Rc les utilise afin de s’assurer, au moment de la compilation, qu’il n’y aura pas de références circulaires (car les références circulaires et le comptage de référence ne font pas bon ménage).

Les catégories existantes sont: Freeze, Send, 'static et Drop.

Les caisses et les modules : programmer dans plusieurs fichiers

Une caisse (crate) est une unité de compilation. Cela signifie que c’est un programme ou une bibliothèque. rustc ne compile qu’une caisse à la fois.

Un module, c’est simplement une sous-partie d’une caisse. Chaque fichier représente un module, mais on peut aussi en déclarer manuellement, cela permet d’avoir le même rôle qu’un namespace de C++.

Mais voyons comment utiliser les définitions contenues dans un fichier dans un autre fichier.

mod truc; // on peut désormais accéder au fichier `truc.rs`

truc::fonction_truc(); // on peut accéder à ce qu’il y a dans truc avec `truc::`

Si on veut pouvoir utiliser ce que contient le fichier truc.rs, on peut importer les noms de fonctions et de variables dans la portée courante.

use truc::fonction_truc; // on peut utiliser fonction_truc directement
// l’import global est expérimental et potentiellement bugué
// il faut placer `#[feature(globs)];` en haut du fichier pour l’activer
use truc::*;

mod truc; // les `use` doivent être placés tout en haut, avant les `mod`.

Ainsi, si vous voulez utiliser std::io::stdin().read_line(), vous pouvez utiliser use std::io; pour utiliser directement io::stdin().read_line(). Dans la bibliothèque standard, les modules de std sont importés par défaut si utilisés, contrairement à extra. De plus, certaines méthodes sont déjà importés, comme std::io::print et ses dérivées.

Quand nous ne sommes plus dans le fichier principal, les use ne marchent plus comme on s’y attend… En effet, les use dépendent du fichier dans lequel on est. Si on est dans truc.rs et qu’on souhaite utiliser des choses de machin.rs, on fera (dans truc.rs) :

use self::machin::Machin; // self se réfère au fichier principal de la caisse
mod machin;

La convention est que le nom d’un module s’écrit en minuscule. Par ailleurs, nommer un fichier de la même façon qu’une déclaration dudit fichier peut causer quelques problèmes.

Pour créer des modules manuellement, on doit utiliser mod et placer le contenu du module entre accolades :

mod foo {
    fn foo_foo() {}

    mod bar {
        fn bar_bar() {}
    }
}

// pour importer bar_bar, on utilisera `use foo::bar::bar_bar;`

Les extensions de syntaxe

La syntaxe de Rust est relativement simple, d’ailleurs les concepteurs du langage ont beaucoup travaillé dans ce sens en unifiant ou en supprimant des concepts redondants, ou encore en réduisant au maximum le nombre de mots-clés du langage. Cependant, il est parfois tentant d’enrichir la syntaxe de Rust pour des besoins particuliers.

Rust propose de modifier localement sa syntaxe, grace a des extensions de syntaxe. Concrètement, une extension de syntaxe est de la forme nom_de_l_extension!(…), où le contenu des parenthèses a une syntaxe spécifique à l’extension.

La bibliothèque standard inclut plusieurs extensions de syntaxe. println! est un équivalent au printf de C :

let answer = 42
println!("la reponse est {}.", answer);
println!("la reponse est {v}.", v=answer);

En C, printf est implementé par une fonction à nombre variable d’argument, et la vérification du nombre et du type d’arguments s’effectue au runtime. Le println de Rust a quant à lui l’énorme avantage d’être vérifié lors de la compilation. C’est en quelque sorte un mini langage embarqué dans le langage Rust, mais compilé en même temps que lui.

Il existe par exemple l’extension asm!, qui permet au développeur d’intégrer du code assembleur en ligne, comme le fait le C via le mot-clé dédié __asm__.

#[cfg(target_os = "linux")]
fn helloworld() {
  unsafe {
    asm!(".pushsection .rodata
                  msg: .asciz \"Hello World!\"
          .popsection
          lea msg(%rip), %rdi
          call puts");
  }
}
fn main() {
  helloworld();
}

Les extensions error!, warn!, info! et debug! permettent d’ajouter des traces de log, activables et désactivables via une variable d’environnement.

Les extensions de syntaxe offrent des possibilités gigantesques, et cela sans perturber le langage. Il est par exemple prévu d’implémenter une extension de syntaxe pour les expressions régulières, ce qui permettrait d’avoir des regex compilées en même temps que son programme, et donc à la fois optimisée et vérifiées à la compilation !

Enfin, il est possible à un développeur Rust d’écrire ses propres extensions de syntaxe. On appelle cela des macros. Attention, le terme macro se rapproche ici beaucoup plus des macros de Lisp que des macros du C. Les macros permettent de définir sa propre syntaxe, et de spécifier quel sera le code généré à partir de cette syntaxe.

Par exemple, un utilisateur de Rust a écrit sa macro range_type!, qui permet de définir simplement un type numérique restreint à une plage de valeur :

range_type!(Percent(float) = 0.0 .. 1.0) // définit le type Percent, qui est toujours compris entre 0 et 1

Une autre macro est même capable de parser du HTML simple :

let _page = html! (
    <html>
        <head><title>This is the title.</title></head>
        <body>
        <p>This is some text</p>
        </body>
    </html>
); // ceci est du Rust valide !

Créer ses propres macros

Il peut arriver que l’on soit obligé d’écrire beaucoup de code redondant, du style :

match entrée_1 {
    special_a(x) => { return x; }
    _ => {}
}
// ...
match entrée_2 {
    special_b(x) => { return x; }
    _ => {}
}

Le système de macros permet de supprimer le code redondant. Par exemple, le code suivant est équivalent au premier :

// `macro_rules!` pour indiquer qu’on va créer une macro
// retour_tôt est le nom de la macro qu’on utilisera pour l’appeler
macro_rules! retour_tôt(
    ($inp:expr $sp:ident) => (
        match $inp {
            $sp(x) => { return x; }
            _ => {}
        }
    );
)
// …
retour_tôt!(input_1 special_a);
// …
retour_tôt!(input_2 special_b);

Plus précisément, les macros permettent de générer du code à la compilation. Ainsi, l’exemple ci-dessus va générer les deux fonctions de départ (strictement les mêmes).

Le $ indique une variable (un peu comme en PHP). Cette syntaxe spéciale permet de différencier le code de la macro et le code Rust en lui-même.

Je ne rentrais pas dans les détails, mais le ($inp:expr $sp:ident), c’est comme la définition des arguments d’une fonction, ça indique le « type » de ce qu’on va donner comme argument. Ici, ça indique que inp est une expression et sp un identifiant de variable (on ne peut donc pas faire n’importe quoi dans les macros).

Mais on peut vraiment faire des choses poussées, plus d’informations sur la documentation.

Outils

Attributs

Les méta-données concernant le code sont passées au compilateur et au générateur de documentation par le biais d’une syntaxe spéciale : les attributs.

#[test]
// la fonction est un test unitaire (voir plus bas pour les détails)

#[crate_type = "lib"];
// pour compiler en tant que bibliothèque

#[license = "MIT/ASL2"];
// Le point-virgule indique un attribut global
// les autres attributs ne s’appliquent qu’à la déclaration suivante
// cet attribut sert à la documentation et aux paquets (voir plus bas)

#[desc = "Projet Machin"];
#[author = "Jean Dupont"];
// autres attributs pour la documentation

#[cfg(target_os = "linux")]
// la déclaration qui suit est ignorée si le système sur lequel on compile
// n’est pas basé sur Linux.

#[cfg(target_arch = "x86")]
// la même chose, mais concernant l’architecture matérielle

Tests unitaires

Pour écrire des tests unitaires, il suffit de placer #[test] sur la ligne précédant une fonction. La fonction ne doit prendre aucun argument et ne rien renvoyer. Si on souhaite que la fonction échoue, il faut mettre en plus #[should_fail].

Les fonctions check, fail, assert (ainsi que assert_eq, assert_approx_eq, etc) sont très utiles pour les tests unitaires.

#[test]
fn test_quelques_opérations() {
    let x = ~[10];
    x.push(5);
    x.pop();
}

#[test]
#[should_fail]
fn test_l’échec_hors_des_limites() {
    let v: [int] = [];
    v[0];
}

Il existe un type de tests unitaires un peu spécial : les benchmarks (tests de performances). Il faut utiliser l’attribut #[bench] mais aussi un peu plus que ça…

#[bench]
fn test_trucmuche(b: &mut extra::test::BenchHarness) { // on va utiliser l’argument
    // le code de préparation
    do b.iter() {
        // le code dont vous souhaitez mesurer les performances
    }
}

De la même façon que le code qu’on compile ou non en fonction de la plateforme, il existe un mécanisme similaire pour les tests unitaires. Il faut utiliser #[ignore())], par exemple #[ignore(cfg(target_os = "win32"))].

Ensuite, il faut utiliser rustc avec l’option --test :

# rustc --test main.rs -o tests

Vous pouvez obtenir ça :

# ./tests
running 30 tests
running driver::tests::mytest1 ... ok
running driver::tests::mytest2 ... ignored
... snip ...
running driver::tests::mytest30 ... ok

result: ok. 28 passed; 0 failed; 2 ignored

Ou ça :

# ./test
running 30 tests
running driver::tests::mytest1 ... ok
running driver::tests::mytest2 ... ignored
... snip ...
running driver::tests::mytest30 ... FAILED

result: FAILED. 27 passed; 1 failed; 2 ignored

Il est possible de placer les tests dans un module et de n’exécuter que ceux-ci :

# ./tests mytest1
running 11 tests
running driver::tests::mytest1 ... ok
running driver::tests::mytest10 ... ignored
... snip ...
running driver::tests::mytest19 ... ok

result: ok. 11 passed; 0 failed; 1 ignored

Pour les tests de performance :

# ./tests --bench
running 2 tests
test bench_sum_1024_ints ... bench: 709 ns/iter (+/- 82)
test initialise_a_vector ... bench: 424 ns/iter (+/- 99) = 19320 MB/s

test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured

rustdoc

rustdoc est un outil livré avec Rust qui permet de générer de la documentation à partir des commentaires du code.

Pour qu’un commentaire soit considéré comme de la documentation, il faut utiliser une syntaxe spéciale :

/// Ceci est un commentaire de documentation mono-ligne
/**
 * Ceci est un commentaire de documentation
 * sur plusieurs lignes
 */

La syntaxe utilisée pour la mise en forme est le Markdown (comme sur Linuxfr), et la version HTML est générée avec Pandoc.

Il y a quelques règles cependant :

  • La première phrase d’un commentaire sera prise comme résumé de la suite du commentaire
  • Seuls les titres indiqués avec un seul # (et non plusieurs # ou plusieurs = en dessous du texte) sont interprétés par rustdoc. Cette règle sera assouplie dans le futur.

Il y a également quelques conventions :

La première phrase doit décrire succinctement ce que l’élément fait. Si ça n’est pas suffisant, la suite devra décrire quoi et pourquoi l’élément fait ce qu’il fait, les entrées-sorties, et mentionner sous quelles conditions le code va échouer.

On doit utiliser des titres standards quand le texte devient long : « Arguments », « Return value » (valeur renvoyée), « Failure » (échec), « Example », « Safety notes » (notes sur la sûreté), et « Performance notes » (notes sur la performance). Les arguments doivent être écrit de la façon suivante :

# Arguments

* `arg1` - pour faire tel truc
* `arg2` - pour connaitre telle chose

Enfin, pour écrire du code, on utilise la syntaxe suivante :

Mettez votre code ici
~~~

Les autres façons d’écrire du code en Markdown ne fonctionnent pas (
) ou sont ambigües (quatre espaces devant le code) et peuvent donc ne pas fonctionner.

Pour générer la documentation, rien de plus simple : il suffit d’ajouter #[link(name = "Nom de votre projet")] en en-tête de votre fichier main.rs (ou le fichier principal de votre projet) et d’utiliser la commande rustdoc main.rs. Cela vous donnera une documentation au format HTML.

Mais on peut fournir bien plus d’informations… Je vous laisse jeter un coup d’œil à la configuration utilisée dans la bibliothèque standard de Rust :

#[link(name = "std",
vers = "0.9-pre",
uuid = "c70c24a7-5551-4f73-8e37-380b11d80be8",
url = "https://github.com/mozilla/rust/tree/master/src/libstd")];

#[comment = "The Rust standard library"];
#[license = "MIT/ASL2"];
#[crate_type = "lib"];

#[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://static.rust-lang.org/doc/master")];

Note : en réalité, la syntaxe de documentation n’est que du sucre syntaxique pour dire #[doc = "Description blablabla"].

rustpkg

rustpkg est un outil qui permet de faire des paquets Rust, largement inspiré du gestionnaire de paquets de Go. On peut donner des informations à cet outil grâce aux attributs (par exemple, #[licence = "ma_licence"] et #[link(vers = "mon_numéro_de_version")]).

Un espace de travail valide contient les dossiers suivants :

  • src/, qui contient un dossier par paquet (ex : src/foo/main.rs) ;
  • lib/, rustpkg install va y installer les bibliothèques nécessaires dans un sous-dossier (ex : si libbar est nécessaire à foo, alors elle sera installée à lib/x86_64-apple-darwin/libbar-[hash].dylib) ;
  • bin/, pour les exécutables (ex : bin/foo) ;
  • build/, rustpkg build va y stocker les fichiers temporaires de compilation (ex : build/x86_64-apple-darwin/foo/main.o).

L’ID d’un paquet prend la forme d’une URL (par exemple, github.com/mozilla/rust si c’est un dépôt distant ou /foo/bar/ si c’est un dépôt local). Une version peut être précisée :

  • Un tag (ex : github.com/mozilla/rust#0.3). Dans ce cas, rustpkg va vérifier que le dépôt contient bien un tag nommé 0.3 ;
  • Une révision particulière (ex : github.com/mozilla/rust#release-0.7). Comme ça n’est pas un nombre décimal, rustpkg passe la refspec (ce qu’il y a après le #) au système de gestion de version sans l’interpréter. Cela compte comme un ID de paquet à part entière, là où une nouvelle version peut satisfaire la dépendance envers une ancienne version ;
  • Une révision particulière (ex : github.com/mozilla/rust#5c4cd30f80). La _refspec est également passée directement au système de gestion de version.

Une fois bien paramétré, on a accès aux commandes rustpkg build, rustpkg clean, rustpkg install, et rustpkg test. Autant dire que ça automatise pas mal de choses !

L’état actuel du projet

Gros travaux avant la 1.0

La version 1 du langage arrive à grands pas, et il reste pas mal de travail. Côté développeur, la syntaxe ne change presque pas mais à chaque version il y a des incompatibilités, heureusement très souvent mineures.

Les développements se focalisent sur les corrections de bugs, l’organisation et le nettoyage du code, mais aussi compléter la bibliothèque standard et améliorer les performances.

Cependant il y a aussi un énorme travail à faire sur la documentation, qui, bien que très complète, nécessite plus d’efforts de mise en page. Les tutoriels nécessitent quant à eux beaucoup plus de travail pour les rendre plus accessibles, complets et simples.

Tester (ou faire des trucs sérieux avec) Rust

Pour l’instant, le seul moyen de tester Rust est d’utiliser l’exécutable de la version 0.8 pour Windows, ou de compiler, au choix, la version 0.8 ou la version en cours de développement (conseillée si vous démarrez un projet un peu important en Rust) pour MacOS.

Si vous êtes sous Ubuntu, vous pouvez ajouter un PPA qui fournit plusieurs versions de Rust dont des nightlies.

Pour Arch Linux, Rust a récemment intégré le dépôt [community]. De plus, un des développeurs de Rust a mis en place un dépôt contenant les compilations quotidiennes de la version de développement. Il suffit d’ajouter :

[thestinger]
SigLevel = Optional
Server = http://pkgbuild.com/~thestinger/repo/$arch

à votre /etc/pacman.conf et d’installer le paquet rust-git.

Il est aussi possible dans Gentoo de rajouter le paquet en version 0.8 et développement en utilisant l'overlay rust (ajouter dev-lang/rust dans /etc/portage/packages.keywords) :

layman -a rust
emerge =dev-lang/rust-0.8

Environnements de développement

Coloration syntaxique

Des configurations pour la coloration syntaxique et l’indentation sont disponibles pour Vim, Emacs, Sublime Text 2 et Kate. Il y a aussi un support pour ctags, Eclipse, NetBeans et autres.

Débugueur

On peut également noter qu’il est possible de configurer Eclipse (ou d’autres IDE) pour débuguer du code Rust via GDB et LLDB.

Projets basés sur Rust

Dans cette partie, je vais vous présenter rapidement une sélection de projets réalisés en Rust, déjà parce qu’il y a beaucoup trop de projets différents et d’autre part parce que tous les projets ne sont pas forcément intéressants ou utilisables. Et j’ai d’autres choses à faire dans la vie aussi. :p

Logiciels

Un certain nombre de logiciels bas niveau ont été créés en Rust, démontrant la polyvalence du langage et l’intérêt du langage dans ce domaine. Plusieurs personnes se sont montrées intéressées dans le remplacement du C par le Rust pour le bas niveau, dans le domaine de l’embarqué par exemple. Il y a aussi quelques jeux vidéo en cours de développement (notamment un émulateur NES) ainsi qu’un traqueur de bug.

zero.rs est un projet de moins de 300 lignes qui permet de lancer des programmes Rust sans système d’exploitation. Nécessite seulement l’accès à quelques fonctions C de base (les tâches, les échecs et le ramasse-miettes ne fonctionnent pas).

Deux projets utilisent zero.rs : Rust.ko, un module minimal pour le noyau Linux et rustboot, un système d’exploitation dont la seule fonctionnalité est de peindre l’écran en rouge — ce qui a beaucoup intéressé les développeurs de GNOME.

Nous avons également sprocketnes, un émulateur NES qui sert de preuve de concept, ne vous attendez pas à ce que vos jeux fonctionnent parfaitement dessus. C’est le genre de programme qui devrait fonctionner avec zero.rs.

Enfin, Evict-BT est un système de suivi de bugs.

Bibliothèques et bindings généralistes

Toujours dans une volonté de ne pas réinventer la roue (ou par flemme ?), on trouve des bindings à la pelle mais peu de bibliothèques en Rust.

Dans les bindings, on a SQLite3, PostGreSQL, MongoDB, (écrit par les développeurs de MongoDB eux-mêmes !), Cocoa (le framework objet de Mac OS X), wxWidgets (bibliothèque d’interface graphique basée sur GTK), PCRE (Perl Compatible Regular Expression, expressions régulières compatibles Perl), libpng, OpenCL, etc.

J’ai trouvé deux bibliothèques Rust intéressantes : RustyMem, bibliothèque client pour se connecter à un serveur Memcached et RustyXML, un parseur XML qui fournit une API de type SAX.

Jeux vidéos

L’intérêt de la communauté autour du langage Rust pour faire des jeux vidéo justifie à lui tout seul cette partie ! Il y a déjà pas mal de ressources, et le Rust risque d’être très intéressant dans ce domaine…

Il y a des bindings pour SDL1, SDL2, SFML2, Allegro5, etc. On peut aussi faire de l’OpenGL, on peut utiliser OpenAL et PortAudio.

On a bien sûr quelques bibliothèques Rust, comme kiss3d (moteur graphique 3D simple et stupide — KISS), nphysics (moteur physique temps réel de corps rigides 2 et 3D), cgmath-rs et nalgebra (mathématiques pour les graphismes et la physique temps réel).

Et bien sûr on a des jeux ! On a deux projets de rogue-like, deux moteurs de FPS en développement, un projet de jeu de rôle en 3D, et un jeu de rythme qui est une traduction quasi-directe du C vers le Rust.

Mais je perds mon temps alors que tout cela est mis à jour régulièrement sur le wiki !

Quel avenir pour Rust ?

Le langage D, bien que très prometteur, n’a jamais percé. Pourquoi en serait-il autrement avec le Rust ?

Le langage D, c’est un compilateur officiel au frontal non-libre, qui avait des fuites de mémoire et un développement fermé (au départ, ça se passe sur Github maintenant), une communauté qui dès ses débuts s’est scindée pour développer deux bibliothèques standard incompatibles et un langage dont l’intérêt ne saute pas aux yeux car similaire au C++ à première vue.

Comme vous pouvez le déduire des projets ci-dessus, la communauté derrière Rust est très active. Et c’est sans doute le plus gros point fort du langage pour lui permettre de percer, de décoller rapidement et de ne pas s’essouffler comme le D.

Le développement du langage se déroule entièrement sur Github, au moins 25 commits par jour tous les jours (des fois ça peut atteindre 50 !), des projets sérieux qui se basent sur le langage avec Mozilla derrière, des tas de projets fun aussi, et des dizaines de bindings… Et un langage aussi bon techniquement, sinon meilleur que le D.

En conclusion, même si on parle assez peu de Rust, il est beaucoup plus connu que le D ne l’était au même stade de développement. De plus, il a vraiment beaucoup d’atouts de son côté : exploitation de fonctionnalités peu connues mais qui existent déjà dans un autre langage (pas d’inconnues et d’expérimentations), haut et bas niveau, beaucoup de vérifications à la compilation, une communauté pour le moment restreinte mais active (avec beaucoup de petits projets mais aussi Servo), etc.

Reste à savoir s’il saura s’imposer dans son domaine de prédilection grâce à sa sûreté (face à C, C++) ou ailleurs grâce à sa vitesse (Java, PHP, Python, Ruby…), car les langages actuels sont très implantés.

La communauté Rust

Il y a déjà quelques endroits où l’on peut discuter du Rust : le sous-Reddit r/rust, la liste de diffusion et les canaux IRC (what else?) #rust et #rust-gamedev sur irc.mozilla.org. Ce sont des canaux très actifs, les personnes présentes sont très sympas et se feront une joie de vous aider. Il y a aussi les canaux #rust-internals et #servo pour les développeurs.

Bref, tout cela ne vous dispense pas d’aller lire le putain de manuel (que vous trouverez sur le site de Rust) !

Contribuer

  • faire connaitre le projet ;
  • traduire le tutoriel et la documentation de Rust en français. J’ai commencé à traduire le tutoriel en français ;
  • écrire un binding pour pouvoir utiliser une bibliothèque sympa depuis Rust ;
  • réaliser un projet codé en Rust ;
  • écrire du code : compilateur, bibliothèque standard ou outils du projet.

Conclusion

C’est un langage moderne, lisible, performant. Et surtout, il semble avoir un avenir prometteur.

Mais le mieux, c’est de tester par soi-même !

Aller plus loin

  • # Et go ?

    Posté par  . Évalué à 2.

    Rust est très proche de Go dans ses objectifs tout en étant moins ancien. Quels sont leurs avantages respectifs ?

    BTW je n'aime pas la syntaxe des fonctions qui est différente pour une fonction nommée et pour une closure. Je préfère la syntaxe du javascript sur ce point, qui est plus cohérente.

    • [^] # Re: Et go ?

      Posté par  . Évalué à 7.

      C’est toujours une pente savoneuse de comparer des langages.

      Je dirais que la grande différence c’est la gestion de la mémoire. En Go il n’est pas possible de se passer du garbage collector, ce qui agace ceux qui veulent une gestion plus optimisée de la mémoire. La contrepartie (pour garder la sécurité de leak et d’accès mémoire auquel rust tient énormément), c’est que les notions de "boites"/pointeurs sont plus nombreuses en Rust. L’analyse de cycle de vie d’un objet en Rust peut probablement perturber un développeur débutant.

      Je dirais (avec toutes les précautions oratoires nécessaires) que Rust est surtout attendu par des développeurs C++ qui adorent le C++ mais qui sont frustrés par l’absence de langage de remplacement (même s’il y a le D). Go s’adresse plus à des développeurs Ruby/Python qui voudraient un langage plus performant et plus carré.

      Cela dit, Go et Rust ont effectivement de gros points communs : pas d’objet au sens héritage, parallélisation par co-routines.

      • [^] # Re: Et go ?

        Posté par  (site web personnel) . Évalué à 6.

        C’est toujours une pente savoneuse de comparer des langages.

        J'aime toujours une pinte savoureuse en contorsionnant le langage.

        ce commentaire est sous licence cc by 4 et précédentes

      • [^] # Re: Et go ?

        Posté par  . Évalué à 5.

        les notions de "boites"/pointeurs sont plus nombreuses en Rust. L’analyse de cycle de vie d’un objet en Rust peut probablement perturber un développeur débutant.

        À mon avis, si on explique bien et dans l’ordre ça va. Par exemple au début on apprend ~, ensuite & quand on commence à faire des fonctions, puis @ pour les besoins particuliers (et c’est pas difficile à comprendre ça marche tout seul!) et enfin * si on veut faire du code unsafe, ce dont on n’a absolument pas besoin quand on est débutant (sauf si on veut faire un fork bomb :p).

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

    • [^] # Re: Et go ?

      Posté par  . Évalué à 10.

      L'avantage de Rust sur Go, c'est de ne pas ignorer les 40 dernières années de recherche en informatique (même si, comme dit dans la dépêche, les 15 dernières années ne sont pas reprises dedans).

      • [^] # Re: Et go ?

        Posté par  . Évalué à 1.

        Tu penses à quoi qui date des 15 dernières années et qui est pas inclus ?

        • [^] # Re: Et go ?

          Posté par  . Évalué à 7.

          le match qui déchire ça race?

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

          • [^] # Re: Et go ?

            Posté par  . Évalué à -4.

            Je vois vraiment pas ce que ça a de si révolutionnaire relativement à un bête switch.

            • [^] # Re: Et go ?

              Posté par  . Évalué à 5.

              Dans un switch tu compare une variable à des valeurs. Dans un match (ou le given when de perl), tu fait tout et n'importe quoi (vérifier une expression régulière, appeler une fonction, etc).

              Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

            • [^] # Re: Et go ?

              Posté par  . Évalué à 2.

              Lier de nouveaux noms dans une branche.

              Ça c'est révolutionnaire (enfin, il y a 30 ans ça l'était, maintenant c'est juste triste).
              Les langages de programmations courants n'ont quasiment aucune structure pour introduire des disjonctions, par contre les conjonctions il y en a à toutes les sauces. (Quant à passer par une hiérarchie de classes et son visitôr pour introduire une disjonction, vaut mieux être payé à la ligne).

            • [^] # Re: Et go ?

              Posté par  . Évalué à 8.

              Si t’as pas compris l’intérêt du match avec la dépêche, je ne peux plus rien faire pour toi.

              • on n’est pas obligé d’utiliser break;.
              • on peut tester plusieurs valeurs dans une seule branche (plusieurs possibilités ou un intervalle).
              • on peut déstructurer toutes les structures de données du langage (struct, enum, tuples…)
              • le match doit être exhaustif pour compiler.
              • on peut introduire une condition supplémentaire après le motif.

              Un switch n’a vraiment pas grand chose à voir.

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

              • [^] # Re: Et go ?

                Posté par  . Évalué à -3. Dernière modification le 12 octobre 2013 à 01:23.

                Toujours pas convaincu. Tout ça me semble du sucre syntaxique (le coup du Cercle/Rectangle on le faisait en C depuis des décennies hein), qu’on retrouve d’ailleurs pour la majorité dans Go (pouisque le troll est partit de là, autant y revenir ;)). Que ce soit mieux qu’un switch je suis d’accord, je ne vois toujours pas ce que ça a de révolutionnaire pour autant. Si c’est ça les 40 dernières années de recherche en informatique qu’on retrouve dans Rust et pas dans Go, je suis pour le moins déçu.

                • [^] # Re: Et go ?

                  Posté par  . Évalué à 5.

                  Rust = rouille = trucs qui ont fait leurs preuves = pas nouveau. Ce qui est nouveau c’est plutôt de réunir toutes ces fonctionnalités dans un langage. Je connais (très) mal Go, c’est pour ça que je n’en ai pas parlé.

                  Après Rust est vachement plus rapide que Go je crois. Si ça t’intéresse, il y a la FAQ du wiki qui explique ce que Rust a en plus, et cet article qui montre plutôt les similitudes.

                  Tu as aussi cette discussion qui expose des points de vue intéressants: pour l’un Go serait pour remplacer plutôt Java et Python, pour l’autre Go va écraser Rust parce que Google a une force de frappe importante, et que Rust est trop complexe.

                  Je ne comprends pas trop les réactions, je trouve au contraire la syntaxe très simple et j’ai plutôt buté sur les boites (forcément, quand on présente les trois en même temps en anglais et que tu découvres le langage, c’est compliqué). Mais en fait à part ~ qui a un comportement un peu particulier, les autres sont très simples.

                  Après au niveau de la lisibilité, je trouve le code Rust beaucoup plus lisible que le C, C++ ou D (mais c’est peut-être dû en partie à coloration syntaxique différente).

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

                  • [^] # Re: Et go ?

                    Posté par  . Évalué à 6.

                    Pour le coup je comprend un peu les réactions. C'est vrai que c'est un peu plus compliqué de raisonner avec les différent types de pointeurs ~, ~, et & et plus particulièrement avec les région. Mais il faut bien comprendre que c'est ce qui fait une grosse partie de l’intérêt de Rust : il vous oblige a bien réfléchir au la durée de vie des variables et ne compile pas tant que ce n'est pas fait.

                    C'est vrai que ça peut paraître plus facile dans d'autre langages, mais cette facilité à un coût :
                    - La prédictibilité des performances pour les langages full GC comme Java, Go, …
                    - La sécurité et la stabilité en cas d'erreur pour les langages comme C/C++ avec lesquels ça peut très facilement provoquer des erreurs.

                    A voir si on est prêt à le payer le coût. Je ne pense pas que Rust soit le meilleur langage pour tout, mais il a clairement un intérêt si on veut une langage au performance prédictibles à la C++, D, …

                    • [^] # Re: Et go ?

                      Posté par  . Évalué à 4.

                      C'est vrai que ça peut paraître plus facile dans d'autre langages, mais cette facilité à un coût :
                      - La prédictibilité des performances pour les langages full GC comme Java, Go, …

                      Tu as tout à fait raison.

                      Juste pour que ce soit dit, souvent quand le GC fait chier en Java, on l'évite tout simplement en sortant de la heap pour les structures de données ou ca fait un sens. C'est dégueux mais ca fait la moitié du job (l'autre moitié étant le garbage à courte vie créé un peu partout qui reste en young generation et qui rend les perfs en low latency dégueux même en faisant gaffe…).

                      L'approche de Rust est meilleure il n'y a pas photo. Si tu peux garder la facilité d'un GC et son adéquation avec des programmes parallèles, tout en réduisant de quelques centaines de MB/s ce que tu balances inutillement dans la face du GC c'est top.

                  • [^] # Re: Et go ?

                    Posté par  . Évalué à 2.

                    Je ne comprends pas trop les réactions,

                    Pour le coup c’est à ça que je réagissais : (oui, je sais, ce n’est pas de toi)

                    L'avantage de Rust sur Go, c'est de ne pas ignorer les 40 dernières années de recherche en informatique (même si, comme dit dans la dépêche, les 15 dernières années ne sont pas reprises dedans).

                    C’est quand même hyper méprisant pour les concepteurs de Go, et j’aimerais savoir c’est ces quoi ces « 40 années de recherche en informatique » ignorées par Go et pas par Rust qui méritent un tel mépris. Le coup du match ça me semble franchement léger pour une telle affirmation

                    Qu’on se comprenne, je dis pas que le match de Rust c’est de la merde, bien au contraire, et les exemples que tu as donnés sont franchement convaincants dans la catégorie « fonctionnalité pragamatique qui juste marche et qui facilite la vie », je dis juste que si c’est ça les 40 ans de recherche en informatique, c’est franchement décevant.

                    • [^] # Re: Et go ?

                      Posté par  . Évalué à 3.

                      L'avantage de Rust sur Go, c'est de ne pas ignorer les 40 dernières années de recherche en informatique (même si, comme dit dans la dépêche, les 15 dernières années ne sont pas reprises dedans).

                      C’est quand même hyper méprisant pour les concepteurs de Go

                      Bof, 40 ans c'est exagéré mais mon impression sur Go c'est que c'est Limbo renommé et Limbo date de 1995..

                      • [^] # Re: Et go ?

                        Posté par  . Évalué à 2.

                        Bof, 40 ans c'est exagéré mais mon impression sur Go c'est que c'est Limbo renommé et Limbo date de 1995..

                        Comme Java ! Coïncidence ? Je ne pense pas…

                        Please do not feed the trolls

                        • [^] # Re: Et go ?

                          Posté par  . Évalué à 1.

                          Certains utilisateurs de Go disent qu’il serait effectivement un concurrent de Java et Python.

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

                    • [^] # Re: Et go ?

                      Posté par  (site web personnel) . Évalué à 8.

                      j’aimerais savoir c’est ces quoi ces « 40 années de recherche en informatique » ignorées par Go

                      Go n'a pas pas de generics/templates par exemple.

                      J'étais très enthousiaste en lisant les premiers articles sur Go, mais au final il est moins puissant, plus lent et moins "outillé" que Java… Rust semble plus prometteur sur les deux premiers points.

                      Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

                      • [^] # Re: Et go ?

                        Posté par  . Évalué à 2.

                        Pour les outils, on a déjà rustpkg qui mérite encore beaucoup de travail, et les tests unitaires et benchmarks sont intégrés au langage, ce qui est déjà très sympa donc ce côté-là je ne pense pas qu’on ai le même problème que le C++ (avec plusieurs dizaines de bibliothèques frameworks de tests unitaires).

                        Le seul gros truc qui pourrait manquer ça serait une bibliothèque pour la complétion du code, à la manière de Clang. J’imagine que le fait que le compilateur fonctionne de la même façon (frontal + LLVM derrière) permettra dans le futur d’utiliser le parseur du compilateur.

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

          • [^] # Re: Et go ?

            Posté par  . Évalué à 5.

            Nope, t'as pas compris ma question (ou le message auquel je réponds). Là tu me parle d'un truc datant de plus de 15 ans (le pattern matching, par exemple haskell a 23 ans) qui est dans rust mais pas dans go.
            Je demandais un exemple d'un truc de moins de 15 ans qui n'est pas dans rust.

            • [^] # Re: Et go ?

              Posté par  . Évalué à 2. Dernière modification le 13 octobre 2013 à 08:53.

              Je ne connais pas du tout Rust, mais par exemple les GADTs ? (Edit : En fait non, ça a presque 20 ans.)

        • [^] # Re: Et go ?

          Posté par  . Évalué à 1.

          Je pense que son affirmation est un peu trop dure. Go tient compte de certaines choses qui a été fait ces dernières années, le système d'interface par exemple est inspiré de Java.

          Pour les principales fonctionnalités qui à mon sens manquent à Go je dirais:
          - Les génériques/template
          - Mémoire isolée entre les taches
          - Les nombreux petits points qui permettent d’éviter de faire du code erroné (immutabilité par défaut, absence de pointeurs null, …)
          - Garbage Collector optionnel

          Bref Go est a mon avis plus une alternative à Java alors que Rust serait plutôt une alternative à C++. A prendre cette affirmation avec des pincettes bien sur. Faire un parallèle entre deux langages est toujours forcément approximatif.

        • [^] # Re: Et go ?

          Posté par  . Évalué à 6.

          Tu penses à quoi qui date des 15 dernières années et qui est pas inclus ?

          Par exemple aux types dépendants et preuves de programme. On les retrouve dans ATS, un langage dans la même niche que rust mais plus proche de la recherche.

          • [^] # Re: Et go ?

            Posté par  . Évalué à -1.

            Par exemple aux types dépendants et preuves de programme. On les retrouve dans ATS, un langage dans la même niche que rust mais plus proche de la recherche.

            Hello, tu as l'air de t'y connaitre en langage de programmation (beaucoup plus que moi encore une fois)

            Mais est-ce que ça ne contribue pas à la sécurité le fait que le type "humain, trop humain" qui programme comprenne nettement mieux les subtilités (ou justement l'absence de trop de subtilités) dans lequel il programme ?

            Cf ici où j'espère une répons de ta part :-D http://linuxfr.org/news/presentation-de-rust-0-8#comment-1492629

          • [^] # Re: Et go ?

            Posté par  . Évalué à 0.

            Esprit d'escalier :

            Je me rends compte maintenant que si tu ne m'as pas répondu, c'est peut-être tout simplement parce que vu que tu suis voire pratiques toi-même la recherche en la matière, tu as trop de choses à dire pour un simple commentaire en réponse à un commentaire d'un ignorant comme moi.

            Dans ce cas, je m'efface, n'hésite pas à faire un journal voire une dépêche du type :

            Quelles sont les caractéristiques qu'on ne peut pas ne pas exiger en 2013 d'un langage de programmation en un minimum moderne ? (la recherche en informatique ne s'use que quand on ne s'en sert pas)

            Je suis sûr que ça serait passionnant.

            • [^] # Re: Et go ?

              Posté par  . Évalué à 3.

              Ce serait sûrement passionnant, mais je ne suis pas du tout compétent pour le faire, je fais de la recherche en info, mais pas dans les langages de programmation; du coup, je connais, mais de loin. Si quelqu'un qui est vraiment là-dedans veut s'y coller, je lirai avec plaisir.

      • [^] # Go vs C++ (et Rust ?) : Minimalisme versus sophistication

        Posté par  . Évalué à 2.

        L'avantage de Rust sur Go, c'est de ne pas ignorer les 40 dernières années de recherche en informatique (même si, comme dit dans la dépêche, les 15 dernières années ne sont pas reprises dedans).

        Jolie phrase, qui claque bien.

        Un peu d'énervement derrière d'avoir vu ce genre d'arguments mis en avant et semblant mépriser la recherche ?

        Go at Google: Language Design in the Service of Software Engineering

        Introduction:

        Go is efficient, scalable, and productive. Some programmers find it fun to work in; others find it unimaginative, even boring. In this article we will explain why those are not contradictory positions. Go was designed to address the problems faced in software development at Google, *which led to a language that is not a breakthrough research language but is nonetheless an excellent tool for engineering large software projects.***

        URL: http://talks.golang.org/2012/splash.article

        Pour caricaturer moi aussi pour me faire comprendre, je dirais simplement que Go qui apparemment est sinon merdique du moins très peu intéressant, a au moins le mérite de ne pas avoir ignorer la vraie révolution de l'informatique, à laquelle plein de gens ont participé, et dans plein de domaines différents, mais en gros ça revient toujours à la même idée :

        L'informatique est faite pour les hommes et non pas les hommes pour l'informatique.

        Le Macintosh de 1984 avait compris ça. Donc ce principe, c'est pour monsieur et madame Michu n'est-ce pas ?

        Non pas du tout, car les gens qui développent les programmes ont beau être des humains différents aux caractéristiques différentes de madame Michu, ce sont fondamentalement des humains dont il faut s'acharner à comprendre leurs besoins à eux les plus importants plutôt que s'acharner à ajouter toujours plus de sophistication et être au plus près du métal

        Ça c'est la philosophie C++ (les premières fois que j'ai regardé Rust sur http://static.rust-lang.org/doc/master/rust.html , il m'a semblé qu'il partageait cette philosphie, n'hésitez pas à me contredire)

        Simplement, elle est irréconciliable avec Go qui a une philosophilie minimaliste

        Less is exponentially more: Rob Pike on Go and Why C++ Programmers Aren't Flocking to it.

        I was asked a few weeks ago, "What was the biggest surprise you encountered rolling out Go?" I knew the answer instantly: Although we expected C++ programmers to see Go as an alternative, instead most Go programmers come from languages like Python and Ruby. Very few come from C++.

        Personnellement, moi qui connait sans doute très peu de choses des 40+15 dernières années de recherche en informatique, je suis pour l'instant des gens qui préfèrent la voie minimaliste. Et je vois régulièrement passer des projets qui me confortent dans cette voie. Encore aujourd'hui, j'ai vu passer ceci Primary QML Support for the Go Language

        En tout cas, après cet article, les lignes n'ont pas bougé, ou plutôt le clivage s'est aggravé. Les gens qui étaient déjà attirés par Go ont compris pourquoi, et les gens qui appréciaient déjà le C++ n'ont pas été convaincus du tout, voire se sont sentis limite insultés.

        Ma conclusion temporaire est qu'il me parait indispensable d'avoir quelque-chose comme Go pour ceux qui apprécient le minimalisme, et quelque-chose comme C++ en plus moderne (Rust peut-être ? je ne sais pas) pour les gens qui apprécient la sophistication.

        • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

          Posté par  . Évalué à 5.

          Rust est sans doute plus compliqué que Go mais plus sûr et plus rapide, et il est bien plus simple que le C++. En vérité, j’ai l’impression d’avoir du Python compilé typé statiquement, avec des trucs tirés de Go/langages fonctionnels.

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

          • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

            Posté par  (site web personnel) . Évalué à 2.

            Rust est plus sûr que go, mais pour la rapidité, je demande à voir. Est-ce que tu as une source pour affirmer ça ? Il me semblait au contraire que le dernier benchmark que j'avais vu montrer que les performances étaient équivalentes entre ces 2 langages.

            • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

              Posté par  . Évalué à 3.

              J’ai lu quelques trucs par-ci par-là, Go compile très rapidement mais il est moins performant que les autres langages compilés. Après la différence n’est pas très important pour le type de programme auquel Go est destiné (plutôt système/réseau mais pas très bas niveau, sans se prendre la tête).

              En fait, c’est le compilateur par défaut qui produit des exécutables pas très rapides. Visiblement Go avec gccgo est plus ou moins au niveau de Rust, mais il reste encore des tas d’optimisations à faire du côté de Rust donc on verra, mais Rust devrait être plus économe en mémoire vive et un plus rapide.

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

            • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

              Posté par  . Évalué à 1.

              Tout dépend ce qu'on appelle rapide.
              Si c'est de vitesse de compilation dont tu parle, en effet Go est excellent et très loin devant Rust et C++.
              Si tu parles de la vitesse du code généré, Rust est en moyenne(bien sur c'est forcément variable d'un benchmark à l'autre) meilleur que Go.

              • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

                Posté par  (site web personnel) . Évalué à 2.

                Je parlais bien de la vitesse du code généré. J'aurais bien aimé avoir une référence, car il me semblait justement que Go avait les mêmes performances que Rust, voir légèrement mieux, pour le moment.

                • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

                  Posté par  . Évalué à 3. Dernière modification le 14 octobre 2013 à 21:38.

                  Je le répète encore, les benchmark inter-langages sont toujours a prendre avec des pincettes. Il est très facile de produire involontairement(ou non) un code qui avantage un langage ou l'autre.
                  Ceci dit, en moyenne sur les divers benchmarks que j'ai vu Rust était sensiblement plus rapide. Je n'arrive pas a retrouver le lien vers les benchmarks les plus précis, mais les premiers résultats que me donne google sur une recherche 'rust go benchmark' semblent confirmer cela en gros.

                  Les benchmarks ou Rust est en retrait par rapport aux autres langages, sont en général ceux qui font beaucoup d'opérations d'entrée/sortie. Sur ce point là en effet Rust est encore en gros travaux, le module std::io devrait être complètement revu d'ici la sortie de la version 1.0. Il devrait être remplacé par le module std::rt::io (encore expérimental pour le moment) qui s'appuie sur le nouveau runtime.

        • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

          Posté par  . Évalué à 6.

          Se tenir au courant des 40 dernières années de recherche ne signifie pas rendre le langage plus complexe pour autant (note que le 40 et le 15 étaient plus symboliques qu'exacts). C'est éviter de laisser de côté des trucs dont on sait qu'ils marchent sans rendre le langage plus complexe:
          - les types somme
          - pas de pointeurs nuls
          - les types paramétrés (génériques)

          La liste ne prétend pas être exhaustive, mais ce sont des choses qui rendent la vie de l'humain qui programme plus facile, et que Go ignore. C'est dommage, parce qu'il y a de bonnes idées dans Go, mais sans ces trucs là, le langage est inutilement bancal.

          • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

            Posté par  . Évalué à 2.

            D'accord, merci de ta réponse (et de celle d'avant aussi).

            Je suis également persuadé que la recherche est très importante… juste qu'à mon avis le secteur de la recherche le plus important en matière de programmation (sans exclure du tout tous les autres) est celui qui étudie de près le fonctionnement des bipèdes qui programment.

            Juste un exemple, pas pour raconter ma vie, mais pour montrer ce qui se passe tout le temps. Je viens de découvrir le développement d'extensions pour Chrome. Super bien fait, design très soigné, j'avance très bien. Donc en peu de temps, j'ai fait plein de trucs passionants (pour moi en tout cas ;) ) et ensuite là je viens de passer au moins 3h sur un petit truc à la con

            http://stackoverflow.com/questions/19342628/chromes-webstore-server-reject-an-extension-with-error-the-manifest-must-def/19343342?noredirect=1#19343342

            (problème pas encore réglé d'ailleurs).

            Bien sûr, on peut dire que je suis stupide de ne pas comprendre ce que je dois mettre dans le paramètre "version" d'un fichier json. N'empêche que parmi les programmeurs, les gens stupides comme moi sont la norme. Les ordinateurs nous permettent de faire rapidement des choses très sophistiquées… et nous prennent la tête longtemps sur des détails par leur intolérance radicale envers la moindre erreur humaine. Quand moi je lis Notre Dame de Paris de Victor Hugo, et que sur les centaines de pages je tombe sur une faute de syntaxe ou d'orthographe, je n'arrête pas le lire le roman entier brutalement en criant SYNTAX ERROR, VICTOR, TU T'ES PLANTÉ :-)

            • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

              Posté par  . Évalué à 1.

              Je crois que c’est le côté le plus rageant de l’informatique… Déjà faudrait commencer par faire de meilleurs mots d’erreurs.

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

              • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

                Posté par  . Évalué à 0.

                Oui ce serait utile.

                Mais à mon avis, on aura beau prendre les meilleures plumes connaissant l'informatique de la planète, leur demandant d'améliorer les messages d'erreur d'un compilateur C++ existant (pas gcc ce serait trop difficile, llvm), on fera des progrès, mais on sera très loin du compte. On pourra refaire un compilateur C++ en voyant ça comme un des trois objectifs prioritaires, on fera encore plus de progrès, mais on sera encore loin du compte vu la complexité de C++. Après Go ? Je ne sais pas si Go est à peu près à la hauteur de la tâche, mais au moins il est clair que d'un point de vue design, c'était quelque chose de prioritaire dès le début.

                Ce qui est primordial car le design (tolérance aux erreurs d'humains qui ne connaissent pas à fond leur langage, leur environenment de programmation, …) ça peut pas être un truc rajouté après coup.

                • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

                  Posté par  . Évalué à 4.

                  Vu que Rust vérifie pleins de trucs à la compilation, il peut donner plus de mots d’erreurs que les autres langages en général.

                  Après je ne sais s’il donne de vraiment meilleurs mots d’erreurs, mais la syntaxe a plutôt peu d’ambigüités donc c’est sûrement meilleur que C/C++ (ou en tout cas c’est possible d’y arriver). Par contre par rapport à Go je ne sais pas.

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

                  • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

                    Posté par  . Évalué à 0. Dernière modification le 13 octobre 2013 à 14:05.

                    oui mais le jeu c'est pas tellement de savoir qui peut remonter le plus d'erreurs possibles et que la syntaxe soit peu ambigüe pour le compilateur
                    il faut surtout que les erreurs qu'ils remontent soient claires et la syntaxe peu surprenantes pour le bipède
                    et puis il faut que tout l'éco-système derrière comprenne ça;

                    Par exemple là grâce à stackoverflow, j'ai fini par comprendre ce qui se passait
                    Le truc intolérable pour l'ordinateur que j'avais fait, c'était, rendez-vous compte, de mettre des commentaires dans un json.

                    Bon json, je savais grosso-modo que c'était des objets javascripts avec quelques restrictions de bon sens pour que ça s'échange mieux avec les autres langages (j'avais lu JavaScript, The Good Parts) mais ça m'était sorti de l'esprit que les commentaires c'était hors de question… vu que dans mon cas c'était à mon avis pertinent.

                    Oui mais effectivement, le designer de json, moins bête ou du moins instruit par la bêtise de ceux qui ont conçu xml, avait réfléchi à ça :

                    Comments were removed from JSON by design.

                    "I removed comments from JSON because I saw people were using them to hold parsing directives, a practice which would have destroyed interoperability. I know that the lack of comments makes some people sad, but it shouldn't."

                    Suppose you are using JSON to keep configuration files, which you would like to annotate. Go ahead and insert all the comments you like. Then pipe it through JSMin before handing it to your JSON parser.

                    Public statement by Douglas Crockford on G+

                    http://stackoverflow.com/questions/244777/can-i-comment-a-json-file

                    En y réfléchissant, il avait raison de faire ça.
                    Autoriser les commentaires aurait été pire que de les interdire.

                    N'empêche que j'ai perdu énormément de temps sur ce problème ridicule
                    Et pas que moi, il suffit de voir le score considérable de la question sur stackoverflow, et le nombre de réponses ayant un score élevé

                    QUESTION : Score 895 : Can I comment a JSON file? If so, how?
                    Score 515: I don't believe you can have an actual comment. (…)

                    Score 216: No, comments in JSON are not allowed, see RFC et json.org (..)
                    Score 126 : It's not allowed because people would do bad things, but it useful if you know what you do. So I just released JSON.minify() which strips out comments and whitespace from a block of JSON and makes it valid JSON that can be parsed. (…) See my very long blog post here http://blog.getify.com/json-comments/

                    Score 70: I've found a little hack that allows you to place comments in a JSON file that will not affect the parsing, or alter the data being represented in any way.

                    Score 50 : Comments were removed from JSON by design. (j'en parle au-dessus)

                    Et d'autres encore.

                    En tout cas ça montre qu'il y a un réel problème d'utilisabilité là…
                    Auquel pourtant le designer avait très bien pensé, mais pas jusqu'au bout…
                    Qu'est-ce qu'il aurait du faire de plus en fait ?

                    • Une page ad-hoc sur le site officiel de json expliquant pourquoi envoyer des données via json serait une idée horrible
                    • Un paragraphe indiquant qu'un truc comme jsonminify par contre serait bienvenu et qu'il peut lister sur une page à part les logiciels bien fait qui font ça
                    • Une note à l'intention des gens qui font les parseurs de json pour leur dire que interdire les commentaires, qui sont naturels quand on connait le javascript, va surprendre un tas de gens qui utilisent json sans connaitre tous les tenants aboutissant. Et donc, qu'il faut qu'ils fassent un message d'erreur explicite renvoyant vers la page de son site

                    Bref, beaucoup de tremblement d'ailes de papillons qui évitent qu'un tremblement de terre ne se déclenche à tout instant à des centaines de kilomètres de là.

                    D'où ma question : pour penser à tous ces petits détails extrèmement importants, ne vaut-il pas mieux commencer sur une base minimaliste comme Go l'a fait, quitte à rajouter ensuite les choses qui manquent vraiment ?

          • [^] # Re: Go vs C++ (et Rust ?) : Minimalisme versus sophistication

            Posté par  . Évalué à 6.

            Ben non, ce n’est pas ignoré par les concepteurs de Go. Ils sont au courant de tous ces trucs (tu as même une entrée dans la FAQ pour les types génériques hein). De fait je ne crois pas qu’un seul programmeur au monde n’ait pas été confronté un jour ou l’autre à ces notions. Ce n’est pas parce qu’ils ne sont pas d’accord avec toi sur la pertinence d’intégrer ces trucs que ça en fait des ignares.

    • [^] # Re: Et go ?

      Posté par  (site web personnel) . Évalué à 4.

      J'ai pas testé rust, mais j'avais cru lire ( peut être entre les lignes ) que go a pour vocation de compiler vite, sans doute pour que Google puisse publier encore plus vite du code sur son infra.

      Le c++ n'étant pas le plus rapide à ce niveau la, sans doute en partie du à la complexité de la chose pour les templates ( et les vérifications qu'il faut faire ), et en partie par la maturité de gcc en terme d'optimisations, je pense que go cherche à corriger ça.

      • [^] # Re: Et go ?

        Posté par  (site web personnel) . Évalué à 3.

        Le c++ n'étant pas le plus rapide à ce niveau la, sans doute en partie du à la complexité de la chose pour les templates

        Un des points faibles du C++ en matière de performance est le préprocesseur, tout bêtement. C'est quelque chose qu'il aurait fallu abandonner, en ne fournissant qu'un support limité à des modules dédiés chargés de faire l'interface avec C.

      • [^] # Re: Et go ?

        Posté par  (site web personnel) . Évalué à 3.

        Je confirme, go compile vraiment très rapidement.

        Par exemple, pour les programmes en go sur lesquels je code (quelques milliers de ligne au code tout au plus), je n'utilise que go run. Ça compile à la volée le code puis exécuté le binaire généré dans la foulée, et c'est instantané même sur des PC pas récents.

        Et c'est, paraît-il, encore beaucoup plus impressionnant sur des programmes bien plus conséquents.

        • [^] # Re: Et go ?

          Posté par  . Évalué à 3.

          Effectivement, Go a été en partie conçu pour ça.

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

          • [^] # Re: Et go ?

            Posté par  . Évalué à 3.

            J’en profite pour rajouter que le compilateur Rust fonctionne bien mais n’est pas du tout optimisé, tout le code n’est pas mis à jour en permanence donc il reste forcément du vieux code (Rust évolue vachement vite). Bref, je ne pense pas que rustc compile aussi vite que le compilateur de Go mais il y a encore de la marge d’amélioration.

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

  • # Je suis sceptique sur les langages multi-paradigmes

    Posté par  . Évalué à 1.

    L'idée est peut-être d'avoir un langage qui a les avantage des autres, mais on en récupère aussi les inconvénients … D'autre part, l'approche objet est loin d'être la meilleure approche dans tous les cas, et mélanger objet/fonctionnel ne me parait pas très cohérent.

    Si je prens l'exemple d'Erlang, le paradigme fonctionnel (ainsi que l'alocation unique d'une valeur à une variable) a été choisi parce que ce langage est orienté processus et permet d'éviter beaucoup de problème lors de l'exécution parallèle des fonctions (on prend garde cependant à isoler les fonctions ayant des effets de bord).

    N'est-ce pas "casse-gueule" de tout mélanger comme ça ? Je précise que je ne suis pas développeur pro, juste un developpeur amateur et intéressé par toute sorte de langage, et que l'avis objectif de "pros" m'intéresse.

    • [^] # Re: Je suis sceptique sur les langages multi-paradigmes

      Posté par  . Évalué à 10.

      Cela réussi bien pour Scala, qui est un joyeux mélange des deux également, même si le langage pousse fortement à préférer les solutions fonctionnelles partout où c'est possible.

      • [^] # Re: Je suis sceptique sur les langages multi-paradigmes

        Posté par  . Évalué à 0.

        Ce n'est pas le langage en lui même qui aura des problèmes, mais la façon qu'aura le développeur à l'utiliser. Et c'est là que je pense que ça risque de poser problème.

        • [^] # Re: Je suis sceptique sur les langages multi-paradigmes

          Posté par  . Évalué à 3.

          Perso j’ai pas eu de difficultés particulière alors que j’ai jamais utilisé de langages fonctionnels. Toutes les fonctionnalités de Rust se marient bien ensemble (en tout cas pour ce qu’on a présenté dans la dépêche).

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

  • # dev français ?

    Posté par  (site web personnel) . Évalué à 2.

    Est-ce qu'il y a des dev français pour le langage ?

    Qu'est-ce qui est mieux foutul que Ocaml vu que le codeur principal connaissait ce langage ?

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

    • [^] # Re: dev français ?

      Posté par  . Évalué à 4.

      L'existence du multithreading à mémoire partagée plutôt que seulement passage de messages ?

      • [^] # Re: dev français ?

        Posté par  (site web personnel) . Évalué à 4.

        L'existence du multithreading à mémoire partagée plutôt que seulement passage de messages ?

        Je ne dirais pas que c'est mieux foutu: en Rust il semble il y avoir trois ou quatre sortes de référence mémoire, en OCaml on ne se préoccupe tout simplement pas de ce genre de choses. Un équilibre différent a été choisi, mais ce n'est pas «globalement mieux foutu».

        • [^] # Re: dev français ?

          Posté par  . Évalué à -1.

          Bah si on veut passer des trucs on utilise des boites uniques. Il est aussi possible de passer des boites partagées selon certaines conditions. Dans 90% des programmes on n’utilisera que ~ et &.

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

          • [^] # Re: dev français ?

            Posté par  (site web personnel) . Évalué à 5.

            Dans 90% des programmes on n’utilisera que ~ et &.

            Peu importe: même si tout ce qu'il faut savoir se résume à cette simple phrase (celle que je cite): pour obtenir le ramasse-miette qui fonctionne dans chaque thread (avantage de Rust sur OCaml) il faut accepter de travailler avec un langage plus compliqué puisqu'il distingue plusieurs sortes de références (modèle mémoire de OCaml plus simple).

            Même si ce que tu dis est vrai ce n'est pas une amélioration gratuite: on échange un avantage contre un inconvénient, à chacun d'apprécier l'un et l'autre — ton opinion semble être que l'avantage est plus important que l'inconvénient, mais ça dépend des gens, de l'application, etc.

            • [^] # Re: dev français ?

              Posté par  . Évalué à 1.

              C’est pas tellement plus compliqué, c’est juste que l’apprentissage sera plus long. Et puis maintenant que j’y pense c’est quand un des trucs à apprendre forcément puisque la personne y sera forcément confrontée un jour…

              En tout cas j’espère que ce côté ne va pas faire fuir les utilisateurs de langages tels que Python ou Java. Mais cette flexibilité va faire plaisir aux utilisateurs de C et C++.

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

              • [^] # Re: dev français ?

                Posté par  (site web personnel) . Évalué à 3.

                C’est pas tellement plus compliqué[…]

                Peut-être mais ce qui compte c'est que c'est plus compliqué: il y a un concept supplémentaire, donc une nouvelle source d'incompréhension, une nouvelle source d'erreurs, un nouveau choix à faire dans la conception d'un logiciel, des questions de performance, etc.

    • [^] # Re: dev français ?

      Posté par  (site web personnel) . Évalué à 5.

      OCaml ne percera jamais pour plusieurs raison :

      • Langage trop complexe et trop puissant pour 80%-90% des développeurs : Notion d'ordre supérieur, de pattern matching, de type somme, c'est beaucoup plus compliqué que les notions de if/then/else, d'accès à un tableau, et même d'objet comme sac de donnés et de fonction.
      • OCaml est une galère pour le multithreading, et c'est pas près de changer car les travaux de Luca Saliu vont rendre impossible la communication entre fil d'exécution. Ce qui implique qu'un List.map ne va pas paralléliser automatiquement. Or c'est le grand intérêt des langages fonctionnels : les fonctions sur les listes (map/filter/unique/etc..) qui remplacent les boucles et autres itérateurs des langages impératifs peuvent être parallélisé nativement. C'est ce que fais Scala.

      Ce qu'apporte ce langage par rapport à OCaml ?
      C'est simple, il ne prend que les concepts les plus simples de OCaml, permet du parallélisme adapté à un langage impératif, un aspect système et surtout, surtout, reste accessible à la masse des programmeurs qui n'ont pas un niveau suffisant pour apprécier un langage fonctionnel.

      « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

      • [^] # Re: dev français ?

        Posté par  . Évalué à 3.

        C'est simple, il ne prend que les concepts les plus simples de OCaml, permet du parallélisme adapté à un langage impératif, un aspect système et surtout, surtout, reste accessible à la masse des programmeurs qui n'ont pas un niveau suffisant pour apprécier un langage fonctionnel.

        Honnêtement, demander à la « masse des programmeurs » de décider du modèle de gestion mémoire de chacune de leurs variables, je doute que ce soit couronné de succès.
        (et ce n'est pas péjoratif, d'ailleurs)

        Le problème AMHA est que cette complexité est imposée à l'ensemble du programme, y compris les 95% qui ne sont probablement pas critiques pour les performances.

        • [^] # Re: dev français ?

          Posté par  . Évalué à 8.

          Bah pourtant c’est ce bien ce que font les programmeurs C/C++, sauf que là on n’a pas besoin de faire de free, et on peut en plus utiliser avoir une boite désallouée automatiquement par le ramasse-miettes (pour ceux qui s’en foutent des performances par exemples).

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

      • [^] # Re: dev français ?

        Posté par  (site web personnel) . Évalué à 4. Dernière modification le 13 octobre 2013 à 22:20.

        OCaml est une galère pour le multithreading,

        Ce sujet m'intéresse, donc est ce que tu aimerais développer?

        et c'est pas près de changer car les travaux de Luca Saliu vont rendre impossible la communication entre fil d'exécution.

        Je ne sais pas qui est ce Luca Saliu, que mijote-t-il donc? Qu'est ce que tu veux dire par rendre impossible la communication entre fils d'exécution?

        Ce qui implique qu'un List.map ne va pas paralléliser automatiquement.

        D'autant que je sache, List.map est déjà non parallèle (je ne comprends pas de quel futur tu parles).

        De toutes façons, comme OCaml est un langage impur, cela peut être incorrect de paralléliser un map. Comme dans par exemple:

        let count =
          let n = ref 0 in
          fun _ -> incr n; !n
        
        List.map count [ 'a'; 'b'; 'c' ]
        

        Il me semble que le compilateur ne s'amuse pas à se souvenir si une fonction est «purement fonctionnelle,» si?

        • [^] # Re: dev français ?

          Posté par  . Évalué à 3.

          http://caml.inria.fr/pub/docs/manual-ocaml-4.01/libthreads.html

          "The threads library is implemented by time-sharing on a single processor. It will not take advantage of multi-processor machines. Using this library will therefore never make programs run faster. However, many programs are easier to write when structured as several communicating processes. "

          Il me semble que le compilateur ne s'amuse pas à se souvenir si une fonction est «purement fonctionnelle,» si?
          Je n'écrit pas de compilateur, mais j'espère bien que si, justement, puisque ça permet de paralléliser automatiquement certaines opérations en se rendant compte qu'elles sont totalement indépendantes.
          C'est si je ne me trompe pas le concept derrière l'extension Cilk + d'Intel, spécifiquement le mot-clef spawn.

          • [^] # Re: dev français ?

            Posté par  . Évalué à 2.

            "The threads library is implemented by time-sharing on a single processor. It will not take advantage of multi-processor machines. Using this library will therefore never make programs run faster. However, many programs are easier to write when structured as several communicating processes. "

            Pfff, pourquoi utilise t'on le mot thread pour tout et n'importe quoi?
            Ça serait quand même plus simple si le sens des mots était standardisé..
            Une proposition:
            -process: géré par l'OS, mémoire non partagée par défaut.
            -task: géré par le langage/une librairie, mémoire non partagée par défaut.
            -thread: géré par l'OS, mémoire partagée par défaut.
            -fiber: géré par le langage/une librairie, mémoire partagée par défaut.

            • [^] # Re: dev français ?

              Posté par  . Évalué à 4.

              Two implementations of the threads library are available, depending on the capabilities of the operating system:

              • System threads. This implementation builds on the OS-provided threads facilities: POSIX 1003.1c threads for Unix, and Win32 threads for Windows. When available, system threads support both bytecode and native-code programs.

              • VM-level threads. This implementation performs time-sharing and context switching at the level of the OCaml virtual machine (bytecode interpreter). It is available on Unix systems, and supports only bytecode programs. It cannot be used with native-code programs.

      • [^] # Re: dev français ?

        Posté par  (site web personnel) . Évalué à 3.

        Langage trop complexe et trop puissant pour 80%-90% des développeurs : Notion d'ordre supérieur, de pattern matching, de type somme, c'est beaucoup plus compliqué que les notions de if/then/else, d'accès à un tableau, et même d'objet comme sac de donnés et de fonction.

        Ça dépend aussi du parcours suivi : OCaml est le premier langage que j'ai appris, du coup je me rappelle qu'après au début les langages impératifs me faisaient un effet bizarre, j'étais surpris de pouvoir sortir d'une boucle avec un break, ou de pouvoir retourner un valeur de n'importe où dans une fonction.

        OCaml a des fonctionnalités un peu complexes autour du système de types, mais c'est assez simple et carré, et une fois les bases acquises (ça prend un peu de temps), on apprend vite à à connaître globalement bien le langage (un peu comme Scheme et ses dialectes) à part juste quelques trucs spéciaux, contrairement à d'autres langages comme Perl (ou C++, et je dirais même Rust peut-être), où on arrive vite à faire des choses sans beaucoup d'apprentissage, mais on continue à être surpris de temps en temps pendant longtemps. Après il y a les langages comme Haskell qui requièrent un bon temps d'apprentissage avant d'être fonctionnel, et en plus on est toujours loin de tout savoir :)

        • [^] # Re: dev français ?

          Posté par  . Évalué à 2.

          Je ne fais pas du Rust depuis très longtemps mais globalement je trouve le langage très logique, en tout je n’ai pas eu de surprises du genre (C++): Ma_classe mon_objet(); ne compile pas parce que le compilateur crois que mon_objet est une fonction. Pas de conversion de types sans qu’on te demande ton avis. Et pleins de trucs comme ça, parce qu’en fait le langage est assez strict donc il y a vraiment peu d’implicite.

          En général quand je découvre des trucs c’est plutôt positif. ^ Et si tu trouves un truc louche, tu peux contacter les développeurs, tant que le langage n’est pas sorti en version stable, et ça c’est vraiment sympa.

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

        • [^] # Re: dev français ?

          Posté par  . Évalué à 10.

          Après il y a les langages comme Haskell qui requièrent un bon temps d'apprentissage avant d'être fonctionnel

          Quel comble.

      • [^] # Re: dev français ?

        Posté par  . Évalué à 3.

        Langage trop complexe et trop puissant pour 80%-90% des développeurs : Notion d'ordre supérieur, de pattern matching, de type somme, c'est beaucoup plus compliqué que les notions de if/then/else, d'accès à un tableau, et même d'objet comme sac de donnés et de fonction.

        Je te trouve bien défaitiste… Franchement, je pense que tout le système de type d'OCaml est trivial comparé au code qu'il faut produire pour faire un truc aussi fou que trier une liste en Java.

        Please do not feed the trolls

        • [^] # Re: dev français ?

          Posté par  . Évalué à 4. Dernière modification le 14 octobre 2013 à 12:40.

          Je te trouve bien défaitiste… Franchement, je pense que tout le système de type d'OCaml est trivial comparé au code qu'il faut produire pour faire un truc aussi fou que trier une liste en Java.

          Implémenter Comparator et appeler une méthode ? Bin putain c' est pas de la rocket science hein faudrait se poser quelques questions…

      • [^] # Re: dev français ?

        Posté par  . Évalué à 7.

        Je pense que c'est surtout une question de culture et d'enseignement.
        Souvent, les gens n'ont jamais fait (je veux dire, vraiment fait, pas juste vu de loin) de prog fonctionnel de leur vie et se disent "ohlala, la syntaxe n'est pas C-like, c'est trop compliqué !". C'est un peu le même syndrome que "Les maths, c'est compliqué !" sans même avoir lu l’énoncé du problème.
        A ce niveau la, Rust a l'avantage
        1) d'avoir une syntaxe qui ressemble a du C;
        2) de ne pas trop se vendre comme un langage fonctionnel;
        3) d’être amené par Mozilla, et pas par "une bande d'universitaire qui ne savent faire que des langages jouets" (rigolez pas, on me l'a déjà sorti).

        Et pour faire de la parallélisation sur List.map, tu peux utiliser parmap, ça marche très bien. Concernant le boulot de Luca, Il a un modèle de concurrence qui permet de faire des maps parallèles sans soucis. Attends de voir ce qu'il a fait avant de tout balancer. Récemment, il y a aussi eu Spoc qui promet des choses intéressantes et quelques autres avancées. Le parallèle en ocaml n'est pas complètement mort, bien au contraire.

        • [^] # Re: dev français ?

          Posté par  (site web personnel) . Évalué à 6. Dernière modification le 14 octobre 2013 à 07:25.

          3) d’être amené par Mozilla, et pas par "une bande d'universitaire qui ne savent faire que des langages jouets" (rigolez pas, on me l'a déjà sorti).

          J'avais une bonne opinion du milieu universitaire (j'ai fait 5 ans de recherche en maths) en ce qui concerne ses méthodes et sa productivité. Depuis deux ans je travaille dans l'industrie, et à vrai dire la pression et le lien au réel sont beaucoup plus gros à la fac qu'à mon nouveau boulot. Je pense qu'il y a un paquet des gens à qui ça va piquer les yeux de lire ça, mais mieux vaut faire la grimace que de remettre en question un bon préjugé bien crasseux avec lequel tout le monde est d'accord, pas vrai? La vérité, c'est que des gens qui se la touchent au boulot, il y en a partout.

          Et pour faire de la parallélisation sur List.map, tu peux utiliser parmap, ça marche très bien.

          Tu as des informations ou des références sur l'utilisation de parmap sur des gros calculateurs (avec un grand nombre de cœurs).

          • [^] # Re: dev français ?

            Posté par  . Évalué à 1.

            Tu as des informations ou des références sur l'utilisation de parmap sur des gros calculateurs (avec un grand nombre de cœurs).

            Pas vraiment, Je m’intéresse à ces histoires de hautes perfs uniquement d'assez loin, par curiosité. Je sais que le mec de SPOC a un pied dans la communauté HPC. Une vidéo de présentation (en français !) est disponible ici. Techniquement, SPOC fait tourner sur n'importe quoi openCL, donc ça inclut les multi-coeurs pas trop vieux.

        • [^] # Re: dev français ?

          Posté par  (site web personnel) . Évalué à 9.

          parmap fait un fork à chaque appel ! C'est donc réservé à du très gros grain.

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

      • [^] # Re: dev français ?

        Posté par  (site web personnel) . Évalué à 3.

        Les enum de rust sont les types sommes d'Ocaml.

        Les "mouvement de référence" sont un typage linéaire qui permet de rendre le multi tache beaucoup plus facile à écrire. Il me semble que le message passing en utilisant les type linéaire avec un compilateur qui ne fait finalement pas de copie réel est la voie suivi par plusieurs langage. Il me semble que Ocaml va aussi vers l'intégration de type linéaire ce qui va rendre possible d'écrire du code parallèle (multi-process) rapide et simple (en gros, map/reduce, cela serait déjà énorme).

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

  • # Concepts C++14

    Posté par  (site web personnel, Mastodon) . Évalué à 5.

    Les concepts seront livrés avec C++14. Il est à noté que GCC implémente déjà cette fonctionnalité dans ses récentes versions. Voir : http://channel9.msdn.com/Events/GoingNative/2013/Opening-Keynote-Bjarne-Stroustrup

  • # let mul

    Posté par  . Évalué à 3.

    Je trouve qu'indiquer une variable avec let mut est très chiant, ça aurait été bien plus simple de n'utiliser qu'un seul mot, du genre mut ou var.

    « 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: let mul

      Posté par  (site web personnel, Mastodon) . Évalué à 10.

      Je pensais aussi ça au départ, mais en fait, c'est volontairement pensé pour augmenter l'effort nécessaire à rendre une variable modifiable. J'aurais cependant préféré que ce langage évite les abréviations, quitte à être encode plus pénible pour toi : let mutable.

      • [^] # Re: let mul

        Posté par  . Évalué à 3.

        Dans ce cas là, autant écrire uniquement mutable, ça permet de différencier beaucoup plus facilement.

        « 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: let mul

          Posté par  . Évalué à 4.

          Oui mais c’est long, tu remarqueras d’ailleurs que tous les mots-clés sont court, le meilleur exemple c’est priv et pub par rapport aux classiques public et private. Je trouve ça très agréable tout en étant lisible car les mots-clés sont systématiquement colorés.

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

          • [^] # Re: let mul

            Posté par  (site web personnel, Mastodon) . Évalué à 0.

            Oui, ça va encourager les débutants à ne pas utiliser d'abréviations. Je crois que je préfère encore la convention d'ObjC, avec une utilisation de symboles plutôt que de mot-clés pour indiquer l'accessibilité d'une donnée membre.

            • [^] # Re: let mul

              Posté par  . Évalué à 5.

              Il me semble qu'il y a déjà largement assez de symboles en Rust….

          • [^] # Re: let mul

            Posté par  . Évalué à 5.

            Oui mais c’est long

            Mais je réponds à un commentaire qui dit qu'il vaut mieux que ça soit et propose let mutable. Comme je le dis, j'aurais préférer mut tout court, c'est bien plus facile de faire la différence.

            « 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: let mul

              Posté par  . Évalué à 4.

              Ce que je voulais dire, c’est que quand on écrit:

              let x = 5;
              let mut y = 2;

              Le let indique une déclaration de variable dans tous les cas, et le mut en plus permet de distinguer les deux déclarations. Après c’est une manière comme une autre de distinguer les deux.

              Je pense que c’est comme cela par logique: en C++ on déclare une variable, et on peut dire qu’on a pas le droit de la modifier. Ici on déclare et on peut éventuellement s’accorder des droits supplémentaires si c’est nécessaire.

              Bref, une question de sémantique avant tout. let = «soit une variable», mut = «mutable». Du coup, «soit x une variable qui vaut 5» et «soit y une variable mutable qui vaut 2».

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

              • [^] # Re: let mul

                Posté par  . Évalué à 1. Dernière modification le 20 novembre 2013 à 10:57.

                j'aurais bien aimé

                let x mutuable = 1.0f32;

                ça sonnerait bien :)

      • [^] # Re: let mul

        Posté par  (site web personnel) . Évalué à 4.

        Exactement, des variables mutables, c'est le mal™.

        « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

        • [^] # Re: let mul

          Posté par  (site web personnel) . Évalué à 3.

          D'ailleurs on va les renommer en constantes.

          Python 3 - Apprendre à programmer dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

    • [^] # Re: let mul

      Posté par  . Évalué à 4. Dernière modification le 11 octobre 2013 à 17:56.

      (Edit: grillé) C’est probablement pour qu’on n’utilise pas des variables mutables, vu qu’elles sont très chiantes pour le compilateur :-)

    • [^] # Re: let mul

      Posté par  . Évalué à 5.

      J’ai écris pas mal de code Rust (enfin pas mal de petits trucs simples pour tester le langage, mais quand même), et ça ne m’a pas du tout gêné. J’ai au contraire beaucoup apprécié le fait que par défaut mes variables soient non-mutables et mes méthodes n’affectent pas l’objet, c’est vraiment génial parce qu’on met vraiment en mut uniquement ce dont on a besoin.

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

      • [^] # Re: let mul

        Posté par  (site web personnel) . Évalué à 8.

        Si on veut limiter les problèmes et simplifier la programmation fortement concurrente, il est important que les variables soient par défaut immutable comme en Erlang. La déclaration explicite ici me semble une très bonne chose. Ensuite, pour la performance, il faut parfois savoir aussi éviter les copies, comme le futur 'move' du C++ et le tout mutable par adresse du Fortran. Bref, pas facile de construire un langage équilibré mais pour le web et la robustesse aux threads et autres coroutines, l'immutable par défaut me semble de plus en plus une bonne approche.

        C'est aussi une approche très fonctionnelle écrite avec une typo plus déclarative !

        • [^] # Re: let mul

          Posté par  . Évalué à 3.

          C'est aussi une approche très fonctionnelle écrite avec une typo plus déclarative !

          C’est exactement ce que je voulais mettre dans la dépêche mais je ne trouvais pas les mots, donc j’ai abandonné. Je trouve ça beaucoup plus lisible mais c’est sans doute parce que j’ai jamais utilisé aucun langage fonctionnel, c’est pour ça que je pense que pour ceux qui ont appris un langage procédural et impératif, ça doit être beaucoup plus facile d’apprendre le Rust que l’Haskell ou l’OCaml. En plus c’est des syntaxes qui font un peu peur au premier abord je trouve, même si après avoir pas mal testé le Rust j’adore toutes les fonctionnalités venant des langages fonctionnels! :)

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

        • [^] # Re: let mul

          Posté par  (site web personnel) . Évalué à 5.

          comme le futur 'move' du C++

          Pourquoi tu dis « futur » ? C'est déjà dans C++11.

    • [^] # Re: let mul

      Posté par  . Évalué à -2.

      Ca serait plus simple surtout de virer le let. A quoi sert-il ?
      x = 1;
      mut x = 1;

      • [^] # Re: let mul

        Posté par  . Évalué à 10.

        Ça permet de savoir quand est-ce que c’est la déclaration, c’est plus facile à analyser syntaxiquement et à trouver avec grep ou simplement une recherche plein texte. Ou simplement l’humain, qui cherche les déclarations cherchera let et non int, float ou ArrayList<ArrayList<TrucÀLaCon>>, ou le nom de la variable (ce qui est encore plus chiant à trouver). Surtout que le mot-clé est tout le temps mis en surbrillance, contrairement à des noms de classe etc qui ne seront mis en avant que dans les IDE (et il n’y en a pas encore pour Rust de toute façon).

        Bref, humain ou machine, même combat.

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

        • [^] # Re: let mul

          Posté par  . Évalué à 3.

          Ok je comprends, ça permet de savoir que c'est la première utilisation.

          Je comprends pas l'intérêt de :
          let mut x = 1.0; // sans indication, le type déduit est f64
          x = 1f32; // le compilateur devine que x est en fait un f32

          Pourquoi ne pas faire let mut x = 1f32 dès le début ?

          • [^] # Re: let mul

            Posté par  . Évalué à 2.

            Disons que tu peux modifier le code suivant la déclaration et ça change le type. Je ne vois pas trop l’intérêt non plus, mais peut-être que j’en trouverais un, et ça montre que l’inférence de type de Rust est assez puissante quand même.

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

            • [^] # Re: let mul

              Posté par  . Évalué à 2.

              C'est surtout que (dans la plupart des cas) t'as pas besoin d'indiquer explicitement le type : le littéral 1.0 peut être soit de type f32 soit de type f64. Ensuite le compilo infère en fonction de l'utilisation qui en est faite. Comme, dans la plupart du temps, t'auras toujours un élément qui te donne le type (tu le retourne et le type de retour de ta fonction est fixé, tu l'ajoute à un nombre passé en argument, tu le passe en argument à une fonction…), l'intérêt est qu'en pratique t'as quasiment jamais besoin de spécifier le type du machin. Pourtant il est bel et bien fixé.

              C'est marrant, si tu trouve cool ce genre de trucs et que tu trouve cool le pattern matching, faut vraiment que tu jette un œil à haskell.

              • [^] # Re: let mul

                Posté par  . Évalué à 2.

                Ouais, j’ai déjà fait quelques codes mais j’aime moyennement la syntaxe (sans doute que ça vient avec l’habitude).

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

        • [^] # Re: let mul

          Posté par  . Évalué à 2.

          Oui c'est tres utile: j'utilise beaucoup "final" en Java pour reperer facilement les declarations, mais c'est pas toujours genial genial, notamment si la variable est mutable :)

    • [^] # Re: let mul

      Posté par  . Évalué à 3.

      Moi ce que je trouve chiant c'est d'être obligé d'écrire const ou final devant toutes mes déclarations :)

      Mais je suis d'accord je préfère scala avec var et val est à la fois plus concis et bien plus clair (même si j'aime bien l'idée de l'immuabilité par défaut).

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: let mul

        Posté par  . Évalué à 4.

        Ça se démarque clairement surtout.

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

  • # bashing du D

    Posté par  (site web personnel) . Évalué à 4. Dernière modification le 11 octobre 2013 à 17:22.

    Je me permet d'ajouter une autre vision sur le D. Tu bash complètement ce langage quitte à te tromper.

    D n'est pas un compilateur!
    Les compilateurs ldc et gdc sont libres!
    La communauté du D est également active:
    source: https://github.com/D-Programming-Language/
    forum: http://forum.dlang.org/

    Le problème des nouveaux langages sont ( Rust, D, Go … ):
    - avoir un niveaux conséquent de bibliothèque afin d'être attractive
    - être enseigné au niveaux académique en vu d'être utilisé par le plus grand nombre

    Ton article essaye un peu trop d'écraser par ton seul propos un langage pour mettre un autre en avant!
    Du coup tu perd en objectivité

    Pour avoir un article de bonne qualité et objectif entre D et Rust: http://versusit.org/rust-vs-d

    Désolé pour l'anglais mais au moins la conclusion est courte (commencer par là)

    • [^] # Re: bashing du D

      Posté par  (site web personnel) . Évalué à 9.

      Si Rust a le même genre de pattern matching que Ocaml et que le switch de D est le même que le C, la comparaison est plus que malhonnête.

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

    • [^] # Re: bashing du D

      Posté par  . Évalué à 6.

      Je ne pense pas que l’intention était de basher le D. Mais les précisions sur compilateur et écosystème sont effectivement intéressantes.

      Par contre je trouve l’article terriblement creux et à charge. Il se contente d’un avis totalement subjectif sur la syntaxe (gouts et couleurs…). C’est dommage que ça n’aille pas plus loin car les deux langages sont visent à peu près la même cible.

      Comparer le D et le rust s’adresse essentiellement à des devs issus du C++. Du coup ça aurait été intéressant d’évoquer des différences sur la gestion de la mémoire, références, pointeurs, smart pointers, sémantique de move, garbage collection et voir comment les deux langages se différencient.
      De même comparer l’approche objet. D a choisi un système objet à la C++ enrichi de template à la C++ en mieux foutu, le Rust a choisi les traits. Voilà encore un sujet intéressant.
      Le fait que rust utilise des co-routines là où D utilise des traditionnels threads est vite évoqué, sans développer les avantages/différences entre les deux.

      • [^] # Re: bashing du D

        Posté par  . Évalué à 4.

        Il y a un truc qui est souvent occulté quand on parle de D alors que je trouve ça génial, c'est le fait de pouvoir définir des invariants.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: bashing du D

          Posté par  . Évalué à 1.

          C’est vrai qu’il y a beaucoup de fonctionnalités dans le D. On peut effectivement faire de la programmation par contrat, mais j’ai pas l’impression que la complexité de l’implémentation, l’augmentation de la complexité du langage et le coup en performances (je ne connais pas vraiment l’impact) vaut vraiment le coup. Après j’ai pas testé, je rate peut-être quelque chose de formidable! ^^

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

          • [^] # Re: bashing du D

            Posté par  . Évalué à 3.

            j’ai pas l’impression que la complexité de l’implémentation, l’augmentation de la complexité du langage

            Je ne sais pas trop de quoi tu parle. C'est supporté au moins par le compilateur officiel. Il ne me semble pas que ça ajoute vraiment de la complexité à la syntaxe de D (qui me semble plus simple que Rust).

            le coup en performances

            Ça on s'en fou un peu. L'idée c'est de faire planter ton programme (comme le ferrait un assert), ça n'est généralement pas ce que tu souhaite en prod. À la compilation tu active ou non ces vérifications. Ça sert surtout à avoir beaucoup de vérification lors des tests. C'est grosso modo assez proche des assert mais c'est plus pratique que de faire de l'AOP pour placer les vérifications partout où tu le souhaite.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

            • [^] # Re: bashing du D

              Posté par  . Évalué à 2.

              Je me suis pas penché vraiment sur la question vu que je n’ai fait que du D «basique». Comme milles autres trucs, faudrait que je teste! ^^

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

          • [^] # Re: bashing du D

            Posté par  . Évalué à 3.

            C’est vrai qu’il y a beaucoup de fonctionnalités dans le D. On peut effectivement faire de la programmation par contrat

            Mauvais exemple, ça fait partie des trucs "pas finis" de D: il n'est pas possible d'associer un contrat a une interface les contrats font partie du corps des fonctions.

    • [^] # Re: bashing du D

      Posté par  . Évalué à 8.

      D n'est pas un compilateur!
      Les compilateurs ldc et gdc sont libres!

      C'est qu'il dit est vrai (DMD était jusqu'à récemment bien plus mature que LDC et GDC. Et la communauté DMD avec le coup Phobos vs Tango a probablement fait reculer l'adoption de D de plusieurs années) mais le problème c'est qu'il parle du passé de D alors que Rust n'en est qu'à la version 0.8..

      Ton article essaye un peu trop d'écraser par ton seul propos un langage pour mettre un autre en avant!

      Là aussi, je suis plutôt d'accord, j'avais essayé de corriger l'introduction sur la gestion mémoire de Rust pour que ça fasse moins "fanboy" mais bon ma modif s'est vu écrasée alors ça m'a plutôt découragé de contribuer.

      J'ajouterai comme critique que les exemples de Rust auraient gagné a être compiler avant d'être mis dans l'article: abs mélangeant les int et uint, évidemment ça ne compile pas (ce qui d'ailleurs est un réel progrès par rapport au C/C++).

      Pour avoir un article de bonne qualité et objectif entre D et Rust: http://versusit.org/rust-vs-d

      Un article de bonne qualité?? Comparer des langages en se limitant à la syntaxe du Hello World (en gros), bof.

      • [^] # Re: bashing du D

        Posté par  . Évalué à 1.

        Là aussi, je suis plutôt d'accord, j'avais essayé de corriger l'introduction sur la gestion mémoire de Rust pour que ça fasse moins "fanboy" mais bon ma modif s'est vu écrasée alors ça m'a plutôt découragé de contribuer.

        Désolé que tu le prennes mal, ça n'était pas mon intention. J'ai essayé de décrire le Rust du mieux possible, mais forcément c'est subjectif.

        J'ajouterai comme critique que les exemples de Rust auraient gagné a être compiler avant d'être mis dans l'article: abs mélangeant les int et uint, évidemment ça ne compile pas (ce qui d'ailleurs est un réel progrès par rapport au C/C++).

        J'aurais dû mais je n'ai pas fait tous les codes triviaux.

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

        • [^] # Re: bashing du D

          Posté par  . Évalué à 10.

          Le problème, c'est de dire "il sera impossible de provoquer des fuites de mémoire", c'est faux: "fuite mémoire" c'est un terme générique et le restreindre aux fuites qu'un GC ou les fonctions de Rust peuvent résoudre est faux, un programmeur qui gère mal sa mémoire va provoquer des fuites mémoires et le GC/Rust n'y pourra rien.

          • [^] # Re: bashing du D

            Posté par  . Évalué à 2.

            Effectivement, je n’avais pas pensé qu’il pouvait y avoir des fuites de mémoire même avec un ramasse-miettes. Dans ce cas, j’aurais dû dire «le langage offre les mêmes garanties de libération de mémoire qu’un ramasse-miettes, ce qui protège de la plupart des fuites de mémoire», ou quelque chose comme ça.

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

            • [^] # Re: bashing du D

              Posté par  . Évalué à 2.

              Dans ce cas, j’aurais dû dire «le langage offre les mêmes garanties de libération de mémoire qu’un ramasse-miettes, ce qui protège de la plupart des fuites de mémoire», ou quelque chose comme ça.

              Amusant: c'était plus ou moins ma modif, en précisant que Rust devait offrir des meilleurs performances qu'un GC (sinon ça ne vaudrait pas la peine de s'enm… avec les n-types de pointeur).

              • [^] # Re: bashing du D

                Posté par  (site web personnel) . Évalué à 3.

                Pour les n-type de pointeur, il suffit qu'une spec les classe du plus cool pour le compilo au moins cool, et que le compilateur donne de bon message d'erreur en cas de mauvaise utilisation. Le pire est une mauvaise utilisation silencieuse.

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

              • [^] # Re: bashing du D

                Posté par  . Évalué à 2.

                Tu aurais dû te plaindre dans la «tribune» de la dépêche! :p

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

    • [^] # Re: bashing du D

      Posté par  . Évalué à 2.

      Je ne voulais pas être méchant avec le D mais je parlais de ce qu'on voyait en premier, ce qui est quand même très important.

      Le D est un très bon langage pour ce que j'ai testé, mais le compilateur officiel est mauvais avec une partie non-libre et le développement était pas très accessible (je crois).

      Au contraire, le Rust donne une très bonne première impression je trouve, avec un seul compilateur basé sur LLVM et un développement complètement ouvert sur Github.

      Je parlais juste des raisons qui selon moi ont freiné l'adoption du D.

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

      • [^] # Re: bashing du D

        Posté par  (site web personnel) . Évalué à 5. Dernière modification le 11 octobre 2013 à 18:28.

        Tu compare Rust par rapport à l'état de l'art mais pour le D tu met que ce que tu as retenu de négatif et qui s'est passé y a plusieurs années de cela.Ce qui n'est plus vrai au jour d'aujourd'hui!

        Si tu compare, tu dois te mettre à jour et être objectif.

        Imagine que je compare python 0.1 à perl 6. Python va prendre un sacré coup.

        Tout le développement du D se fait à travers github comme rust les contributions sont les bienvenues, les fork /merge vont bon trains.

        Enfin bon …

        • [^] # Re: bashing du D

          Posté par  . Évalué à 1.

          En effet, je parlais plutôt du passé, ça n'est sans doute pas assez clair dans la dépêche, et j'en suis désolé.

          Si un gentil modérateur pouvait préciser ça clairement, ça serais sympa. ^^

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

        • [^] # Re: bashing du D

          Posté par  . Évalué à 7.

          Le but n’est pas de comparer Rust à D, le but est de répondre à la question « pourquoi les auterus de Rust pensent que Rust peut réussir là où D a échoué ? »

        • [^] # Re: bashing du D

          Posté par  . Évalué à 3.

          au jour d'aujourd'hui!

          "aujourd'hui", suffit (et c'est déjà un pléonasme, techniquement !).

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

          • [^] # Re: bashing du D

            Posté par  . Évalué à 4.

            «hui» suffit alors. xD

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

    • [^] # Re: bashing du D

      Posté par  . Évalué à 10. Dernière modification le 11 octobre 2013 à 20:11.

      Pour avoir un article de bonne qualité et objectif entre D et Rust: http://versusit.org/rust-vs-d
      Désolé pour l'anglais mais au moins la conclusion est courte (commencer par là)

      L'article en question est certainement un des plus mauvais que j'aie pu lire sur le sujet de D et Rust réunis:
      - Il fait une comparaison très superficielle sur la syntaxe, pas de la moindre fonctionnalité.
      - Il utilise une notation inutilement verbeuse pour Rust.
      - Il utilise des syntaxes qui ont disparu il y a longtemps de Rust(a la limite ok) voire qui n'ont jamais existé(traitement d'erreur)

      Le tout pour arriver a une conclusion assez péremptoire, digne d'une cour de récré, et qui ne m'aurait même pas convaincu si je ne connaissait pas le Rust.
      Il y a beaucoup de chose que l'on peut vraiment reprocher à Rust même sur la syntaxe (les régions notamment), il n'en site pas une seule. Cet article donne juste l'impression de voir un supporter de D frustré qui a jeté un coup d'oeil en 30 secondes à Rust pour essayer de trouver des arguments pour le descendre, et encore il n'a pas choisi les bons.

      Autant on peut reprocher des inexactitudes à l'article publié sur LinuxFr en ce qui concerne la situation du D, autant il ne prétend pas faire un comparatif précis des langages, juste de comparer les débuts de chacun des langages.
      L'article que tu cites est complètement partisan, terriblement superficiel et faux de surcroit.

  • # Nested functions en C

    Posté par  (site web personnel) . Évalué à 0.

    Enfin, il est possible d’écrire des fonctions imbriquées (nested functions, fonctions à l’intérieur d’autres fonctions), contrairement au C, C++ ou Java.

    C'est faisable en C ! (bien que fortement découragé et non standard…)
    http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html

    • [^] # Re: Nested functions en C

      Posté par  (site web personnel, Mastodon) . Évalué à 2.

      En C++, il est standard de créer des classes à l'intérieur de fonctions, ce qui permet de contourner cette limitation. Enfin, maintenant on a du suce syntaxique qui évite de créer explicitement une classe dans la fonction (avec les lambdas).

    • [^] # Re: Nested functions en C

      Posté par  . Évalué à 4.

      Sauf qu'en Rust on peut imbriquer plusieurs niveaux.

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

    • [^] # Re: Nested functions en C

      Posté par  . Évalué à 10.

      Ben, si c'est pas du C standard, alors c'est pas « faisable en C »…

  • # Deux petites erreurs

    Posté par  . Évalué à 5.

    Merci pour l'article très complet. Je suis avec intérêt l'évolution de Rust depuis quelque temps maintenant. J'ai cependant trouvé deux petites erreurs dans l'article, qu'il faudrait corriger :

    • Comme tu le dis déjà, les pointeurs managé ne sont actuellement pas gérés par un ramasse-miettes, se sont en effet actuellement des pointeurs intelligents. Mais il y a déjà un "Cycle collector" sur ces pointeurs qui permet de résoudre les références circulaires.
      Un vrai ramasse-miettes devrait cependant permettre d'améliorer les performances a l'avenir.

    • "zero.rs" ne permet pas de supprimer l'OS mais le "runtime" du programme (un morceau de code qui gère le déroulement du reste du code). A noter d'ailleurs que "zero.rs" n'est plus nécessaire. Il suffit actuellement de poser une annotation pour produire un programme sans runtime.

    • [^] # Re: Deux petites erreurs

      Posté par  . Évalué à 3.

      Comme tu le dis déjà, les pointeurs managé ne sont actuellement pas gérés par un ramasse-miettes, se sont en effet actuellement des pointeurs intelligents. Mais il y a déjà un "Cycle collector" sur ces pointeurs qui permet de résoudre les références circulaires.

      C’est ce qu’on m’a dit sur IRC, donc faudrait quand même revérifier, parce que justement on m’a dit que le gros problème actuellement c’était les références circulaires. Ou alors c’est moi qui ai mal compris.

      Un vrai ramasse-miettes devrait cependant permettre d'améliorer les performances a l'avenir.

      Ça je l’ai dit, enfin pas que ça améliorait les performances mais ça me semblait évident.

      "zero.rs" ne permet pas de supprimer l'OS mais le "runtime" du programme (un morceau de code qui gère le déroulement du reste du code). A noter d'ailleurs que "zero.rs" n'est plus nécessaire. Il suffit actuellement de poser une annotation pour produire un programme sans runtime.

      J’ai dû mal comprendre alors, au temps pour moi.

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

      • [^] # Re: Deux petites erreurs

        Posté par  . Évalué à 2.

        En y réfléchissant bien, je pense que ce que la confusion que tu as faite vient du fait que générer du code sans runtime est un prérequis pour faire du code système comme celui d'un OS ou de drivers.

        • [^] # Re: Deux petites erreurs

          Posté par  . Évalué à 2.

          En tout cas, je sais qu’il a eu des discussions sur la liste de discussion pour faire tourner Rust sans OS (ça parlait de faire tourner Rust sur du Barebone).

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

          • [^] # Re: Deux petites erreurs

            Posté par  . Évalué à 5.

            Ce que je voulais dire c'est que "zero.rs" ne retire pas l'OS comme on pourrait le comprendre en lisant ton article. Il permet de retirer du code le "runtime" : le noyau de code dépendant de l'OS qui permet le déroulement du reste du code. Dans le cas de rust, le Runtime permet de gérer notamment les piles segmentées, les Thread légers(Tasks), les I/O, le collecteur de cycle…

            Le fait de ne pas avoir de runtime, permet donc de faire du code de très bas niveau, comme c'est nécessaire pour écrire un driver, un logiciel hors OS, un OS vu que c'est juste un cas particulier d'un logiciel hors OS. C'est donc en effet nécessaire pour tourner sur des microcontroleurs simples utilisés dans les machines embarquées ou du moins ultra-spécifiques.
            Ça permet aussi de s'interfacer plus facilement avec d'autre langages vu qu'il on de fortes chance de ne pas aimer les fonctionnalités du runtime rust(c'était le cas des piles segmentées).

            D'ailleurs, pour info, une grosse partie de la 0.8 et qui est encore en cours consiste à remplacer l'ancien runtime en C++ par un nouveau écrit en Rust. Il devrait avoir un système de planification des Task plus intelligent et améliorer les I/O.

            • [^] # Re: Deux petites erreurs

              Posté par  . Évalué à 1.

              D'ailleurs, pour info, une grosse partie de la 0.8 et qui est encore en cours consiste à remplacer l'ancien runtime en C++ par un nouveau écrit en Rust. Il devrait avoir un système de planification des Task plus intelligent et améliorer les I/O.

              Il y a déjà une bonne partie du runtime en Rust, non? En tout cas, les IO c’est un peu en chantier donc à part l’entrée et la sortie standard, pour le reste c’est compliqué de trouver de la documentation et compliqué à faire fonctionner. Les IO sont un point réellement complexe et c’est pas la première fois qu’il réécrivent cette partie de la bibliothèque standard (autant en profiter, parce qu’avec la version 1.0 on pourra faire beaucoup moins de changements).

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

              • [^] # Re: Deux petites erreurs

                Posté par  . Évalué à 2. Dernière modification le 12 octobre 2013 à 01:32.

                En fait l'ancien runtime a été remplacé par défaut l'ancien, il y a une ou deux version déjà. Mais le nouveau runtime en Rust n'a pas encore toute les fonctionnalités définitives et il en manque encore par rapport à l'ancien, notamment les piles segmentées.

                Pour les I/O si elles sont en chantier, c'est en grande partie parce qu'elles attendaient le nouveau runtime qui devrait leur permettre d'être plus efficace. Dans Rust 0.8, le module "rt::io" qui repose sur le nouveau runtime a été introduit. Il est encore considéré expérimental pour le moment.

  • # Un oubli monumental

    Posté par  . Évalué à 9.

    J’ai oublié de parler de la visibilité des déclarations. C’est ça quand on fait une dépêche important sans marquer ce qu’il reste à faire… :p

    Si on veut accéder à quelque chose déclaré dans un autre fichier, il faut qu’il soit marqué pub (pour publique). Par défaut, tout est privé (on peut l’indiquer en marquant priv).

    On remarquera qu’encore une fois, les mots-clés sont concis mais toujours tout à fait compréhensibles (et c’est vachement agréable au quotidien).

    pub struct Test { // on peut mettre un indicateur de visibilité sur une struct
        x: uint, // mais ça n’a pas de sens en Rust pour un attribut
        y: uint
    }
    
    impl Test { // ça n’a pas de sens non plus pour une `impl`
        pub new(  ) -> Test {  } // mais on peut sur des méthodes
    }
    
    priv trait Truc { // le trait ne sera accessible que dans son fichier
        fn machin(  ) {  } // la méthode est forcément publique dans un trait
    }

    Désolé, j’avais vraiment complètement zappé.

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

    • [^] # Re: Un oubli monumental

      Posté par  . Évalué à 3.

      Pas vraiment un oubli, mais je viens de voir trois projets intéressant en passant sur le sous-reddit de Rust:

      • Rustic Operating System, encore un système d’exploitation!
      • ears, petite bibliothèque pour jouer du son, et utilise OpenAL et libsndfile. (Rappel: ne pas réinventer la roue!)
      • Rust FUSE, pour utiliser FUSE avec Rust (merci captain obvious!).

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

      • [^] # Re: Un oubli monumental

        Posté par  . Évalué à 5.

        Attention ne t’emballes pas trop quand même, RusticOS, est très loin d'être un OS loin de la. Pour le moment c'est juste un POC qui montre qu'il est théoriquement possible de faire un OS en Rust. Il se contente d'afficher un message au boot et de boucler. Il y en avait déjà un du même style qui reposait sur zero.rs si ma mémoire et bonne.

        D'ailleurs pas mal des projets que tu as cité dans l'article sont intéressant sur le papier, mais la certains sont dans un état bien trop primaire pour être déjà vraiment utilisables. Je pense que détailler l'utilisabilité de chacun, n'aurait pas été inutile.

        C'est sympathique de voir qu'il y a pas mal d'activité autour de Rust, mais il ne faut pas non plus laisser croire que tout est déjà opérationnel, on en est encore loin.

        • [^] # Re: Un oubli monumental

          Posté par  . Évalué à 2. Dernière modification le 11 octobre 2013 à 21:58.

          Je crois avoir bien indiqué dans la dépêche que rustboot ne faisait pas grand chose, donc je sous-entendait que celui-là était dans un état aussi «primitif» que le premier.

          Après j’ai pas testé tous les projets — en fait aucun — mais en général c’est clairement indiqué sur la page Github que c’est en cours quoi.

          En fait pour le moment c’est beaucoup de petits projets plus ou moins fonctionnels, faut dire que vu l’état actuel du Rust on n’a pas forcément envie de se lancer dans un gros projet, c’est plutôt des petits hobby qui peuvent donner quelque chose de sympa! Après j’ai quand même l’impression que les bindings au contraire sont plutôt pas mal.

          C’est vrai que dans le feu de l’action, je ne pense pas à préciser ce qui me semble couler de source, vu que je suis le projet depuis un petit peu de temps déjà.

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

  • # Pourquoi un typage dynamique pour avoir plus de sûreté ?

    Posté par  . Évalué à 2.

    Petite question de béotien, quel est l'avantage d'avoir let plutôt que les classiques int, float, double pour faire un langage sur.
    Sans m'y connaitre, j'aurais penser que connaitre le type de la variable est un avantage pour que le compilateur puisse vérifier et optimiser le code.

    • [^] # Re: Pourquoi un typage dynamique pour avoir plus de sûreté ?

      Posté par  . Évalué à 9.

      il ne faut pas confondre inférence de type et fort typage, dans le cas présent le typage est fort mais déterminé lors de la compilation donc il n'en reste pas moins très sur.

    • [^] # Re: Pourquoi un typage dynamique pour avoir plus de sûreté ?

      Posté par  . Évalué à 4.

      Ce n'est pas un typage dynamique, mais une inférence de type. Ça permet de faire beaucoup plus de vérifications statiques (au moment de la compilation). Par exemple :

      c=1
      
      foo(c)
      
      e="toto"
      
      foo(e)
      
      def foo(myvar) {
          // ici tu *dois* (le compilateur n'accepteras
          // pas que tu écrive du code qui marche que
          // dans l'un des cas) prendre en compte le fait
          // que myvar peut être un entier ou une string
      }
      

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Pourquoi un typage dynamique pour avoir plus de sûreté ?

      Posté par  . Évalué à 3.

      let c’est pour dire «tiens c’est une variable», le type d’une variable est déterminé à la compilation, et souvent déduit par Rust mais c’est possible de préciser tout le temps le type.

      Je suis plutôt fan de l’annotation à la fin de la variable pour indiquer son type, par exemple let x = 1u; pour dire que c’est un entier non-signé, mais on peut indiquer de façon plus lisible le type, par exemple let x: uint = 1;.

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

  • # Bonne présentation de Rust

    Posté par  . Évalué à 10.

    Bravo pour cet article, je trouve qu'il donne un bon aperçu de ce langage.

    Je suis de très près l'évolution de Rust depuis sa version 0.2, grâce aux excellents blogs des développeurs, aux comptes rendus des weekly meetings, à la mailing-list des développeurs, et à /r/rust. Toute la conception du langage a été faite publiquement, ce qui est une bénédiction pour moi qui était curieux de savoir comment est conçu un langage de programmation ! Les gens de chez Mozilla ont fait un travail de conception remarquable, en n'ayant pas peur de remettre en cause chacune de leur décision, tout en restant toujours pragmatiques dans leurs choix.

    Rust est un langage très prometteur, j'attends de voir la suite avec impatience !

  • # .... ça me fatigue ce titre obligatoire.

    Posté par  . Évalué à 2.

    Reste à savoir s’il saura s’imposer dans son domaine de prédilection grâce à sa sûreté (face à C, C++) ou ailleurs grâce à sa vitesse (Java, PHP, Python, Ruby…), car les langages actuels sont très implantés.

    C'est un peu toute la question. Que faire avec ce langage pleins de bonnes intentions.
    Cela découle probablement aussi de la manière dont le projet se définit lui même :

    Rust is a curly-brace, block-structured expression language … Its design is oriented toward concerns of “programming in the large”…

    Un langage pour la /beauté/ et la /pureté/ du code afin de servir une meilleure activité de son écriture et de sa maintenance.

    Je me garderais bien de dire si cela est bien ou pas pour l'adoption du langage.

    Maintenant, je me demande à quel vitesse les projets et les technologies se renouvellent dans le monde du développement scientifique, des entreprises et du libre. Ceux qui me semblent être consommateurs de projets écris en c / c++ / java—des concurrents plus appropriés que php.

    A la pensée du noyau linux ou des logiciels, et plus encore des méthodes de travail qui y sont liés, historique de certaines entreprises, je me dis que c'est un processus long, voir très long.

    Le jeu vidéo pour des raisons pas forcément évidente à mes yeux, est un early adopters, il semble s'investir déjà sérieusement dans le projet.
    Une exception dans la masse ?

    Espérons simplement que le langage ne s'y enfermera pas et que malgré sa neutralité à ne servir que l'activité du code et du développement logiciel, d'autres besoins concret trouveront adéquat d'opter pour ce langage lors de leurs éventuels renouvellement.

    Finalement. Si chaque composant d'un projet logiciel doit se concentrer sur une tâche et le faire bien, alors, on est très probablement sur une voie d'avenir : )

    • [^] # Re: .... ça me fatigue ce titre obligatoire.

      Posté par  . Évalué à 7.

      Je pense au contraire que le jeu vidéo est un des domaines ou Rust me parait le plus à ça place.
      Parmi les avantage qui me viennent en tête je vois :

      • Il est assez proche de C++ au niveau des performances (se baser sur l'infrastructure LLVM ça aide).
      • Il ambitionne une gestion plutôt poussée de la concurrence, ce qui est clairement le voie pour profiter de meilleures performances avec les machines modernes.
      • Il reste orienté bas niveau et laisse le choix a l'utilisateur de la façon dont il souhaite gérer chaque élément en mémoire (pile, tas, gc,…)
      • L'utilisation de la mémoire est très surveillée, ce qui permet d'éviter dès la compilation la plupart des erreur liées à la gestion mémoire, ce qui est clairement une des principale sources de bugs dans ce genre programmes.
      • Il y a la possibilité d'utiliser des bloc "unsafe" pour bypasser sporadiquement les contrôles quand on sait ce que l'on fait
      • Possibilité d'utiliser un GC uniquement pour les tache non critique qui ne bloquera pas les autres.
      • Il s'interface assez simplement avec le C, ce qui permet de profiter d'une grande partie des bibliothèques existantes.

      Bref je dirais que Rust offre a la fois la puissance et la finesse d'action de C tout en étant bien plus surveillé par défaut. Mais cette surveillance ce faisant principalement à la compilation, ça n'affecte presque pas les performances finales.

      • [^] # Re: .... ça me fatigue ce titre obligatoire.

        Posté par  . Évalué à 4.

        Ça me parait être surtout un langage de choix pour toute application complexe nécessitant des performances (quand ça seras stable), par exemple les navigateurs web, effectivement les jeux vidéos, mais je suis sûr que ça sera super utile pour des logiciels de traitement d’images ou de vidéos.

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

    • [^] # Re: .... ça me fatigue ce titre obligatoire.

      Posté par  . Évalué à 3.

      Un langage pour la /beauté/ et la /pureté/ du code afin de servir une meilleure activité de son écriture et de sa maintenance.

      Si tu as la dépêche, le but Rust de Rust n’est ni la pureté ni la beauté.

      A la pensée du noyau linux ou des logiciels, et plus encore des méthodes de travail qui y sont liés, historique de certaines entreprises, je me dis que c'est un processus long, voir très long.

      Ça sera là le principal défi, à mon avis la meilleure chose à faire c’est d’améliorer la compatibilité avec les autres langages (et il y a pas mal de travaux pour atteindre ce but actuellement).

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

  • # Rust et ATS

    Posté par  . Évalué à 3.

    Comment se positionne Rust par rapport à un langage qui est un peu sur le même créneau système, ATS ?

    J'ai l'impression que ATS fait comme Rust, mais qu'ATS, lui, a intégré les nouveaux concepts des 15 dernières années (types linéaires, par exemple).

    • [^] # Re: Rust et ATS

      Posté par  . Évalué à 4.

      Les types linéaire sont dans rust, nan ? Enfin les owned pointers (ou un truc comme ça), c'est pas ça sous un autre nom ?

      • [^] # Re: Rust et ATS

        Posté par  . Évalué à 1.

        Ha oui, en effet.

        Les deux langages ont donc encore plus de points communs que je ne le pensais.

        Et donc, pourquoi choisir Rust et pas ATS , ou le contraire, dans ce cas ?

        • [^] # Re: Rust et ATS

          Posté par  . Évalué à 3.

          Et je parlais de types linéaires, mais en fait, ce qui est particulier à ATS, c'est les types dépendants. Là, je crois que Rust n'en possède pas.

    • [^] # Re: Rust et ATS

      Posté par  . Évalué à 7.

      Le but des deux langages est très différent, principalement du au fait qu'ATS n'est qu'a moitié un langage de programmation l'autre moitié étant une theorem prover (comme Coq).
      On peut tracer un parallèle entre Rust/ATS et Haskell/Agda.

      ATS dispose des types dépendants, ce qui veut dire que les types sont manipulables comme des valeurs et que les valeurs peuvent apparaitre dans les types. Ça semble simple dit comme ça, mais ça ne l'est pas du tout. Le typechecking devient très compliqué et demande souvent plus d'annotation, en contrepartie on peut vérifier statiquement bien plus de chose.

      Un exemple : on peut encoder dans les types que les accès tableaux ne sont jamais out of bound. C'est génial ! Oui, mais ca veut dire que dès que tu fais un accès, tu dois prouver (via des manipulations sur les types, qui sont parfois devinée par le compilateur, mais pas toujours) que l'entier que tu utilises est bien dans les bornes. Ca c'est un exemple simple, mais on peut encoder des choses bien plus complexes.

      Je ne sais pas trop comment ATS se comporte vis a vis de la terminaison, mais en Agda, tout programme doit terminer, il sait souvent le deviner tout seul .. mais pas toujours, et dans ce cas, il faut fournir la preuve.

      La, on atteins un point ou il faut choisir : soit tu as de plus en plus de type safety, au prix d'annotations et de convolution parfois véritablement complexes, soit tu laisses passer des choses, mais ton compilateur sait inférer tout ce dont il a besoin.

      Je me suis un peu étalé, mais je pense que ça répond a la question.

      • [^] # Re: Rust et ATS

        Posté par  . Évalué à 2.

        C’est vrai que Rust se décrit comme étant un langage pratique («practical language»), d’ailleurs avant il y avait des typestates qui ont été retirés car trop complexes pour ce que ça permettait de faire.

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

  • # Je suis entrain de tester rust !

    Posté par  (site web personnel) . Évalué à 2.

    J'ai un peu de temps libre en ce moment et du coup, je me suis dit que j'allais découvrir Rust.

    Le premier point qui m'a marqué c'est le manque d'outils de base. Rien, quasi peanuts, pas de paquets debian, ubuntu ou tout autres distributions. Ça ne favorise pas l'essai et l'adoption. Après, l'installation en suivant le wiki est tout de même assez facile.

    Bon, j'essaie de faire un peu de compression statique et je reviens pour la prochaine dépêche avec plus de truc à dire.

    La réalité, c'est ce qui continue d'exister quand on cesse d'y croire - Philip K. Dick

    • [^] # Re: Je suis entrain de tester rust !

      Posté par  . Évalué à 4.

      Mais qu’est-ce que j’ai encore foutu…

      Dépôt pour Ubuntu (pour Debian je ne sais pas): https://launchpad.net/~hansjorg/+archive/rust (stables + nightlies)

      De plus, rust est dans le dépôts [community] d’Arch Linux depuis peu (version stable).

      Si un gentil modérateur pouvait rajouter ça à la dépêche, siouplé!

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

    • [^] # Re: Je suis entrain de tester rust !

      Posté par  . Évalué à 1. Dernière modification le 12 octobre 2013 à 14:18.

      Tout dépend de ta distribution : https://www.archlinux.org/packages/community/x86_64/rust/

      Bon par contre, le build s'est fait avec : ./configure --prefix=/usr --disable-docs mais bon, elle n'est pas très difficile à trouver.

      Forcement, j'ai tellement mis de temps à re-lire l'article que j'ai posté sans avoir vu la réponse au dessus pour la Arch.

  • # Quel type

    Posté par  (site web personnel) . Évalué à 4.

    Juste une petite question sur l'un des exemples. Que se passe-t-il si les guillemets sont retirés autour de -18 dans l'exemple suivant ?

    let est_majeur = true;
    // Pas besoin d’opérateur ternaire
    // En C++ ça donnerait:
    // int x = (est_majeur)? "+18": "-18";
    let x = if est_majeur { "+18" } else { -18 };

    « IRAFURORBREVISESTANIMUMREGEQUINISIPARETIMPERAT » — Odes — Horace

    • [^] # Re: Quel type

      Posté par  . Évalué à 5.

      if and else have incompatible types: expected `&'static str` but found `<VI0>` (expected &'static str but found integral variable)
      

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

      • [^] # Re: Quel type

        Posté par  . Évalué à 4.

        C'est là ou on voit bien que Rust est un langage à typage statique bien que l'inférence de type peut donner l'impression inverse.

        Pour info le dans le message d'erreur indique un type que le compilateur n'a pas encore été totalement déterminé par inférence. Ils auraient pu trouver une notation plus parlante mais on s'y fait vite.

      • [^] # Re: Quel type

        Posté par  (site web personnel) . Évalué à 2.

        Je trouve un peu bizarre que le compilateur soit pas fichu de détecter qu'on a mis un entier et pas un <VI0>

        « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

        • [^] # Re: Quel type

          Posté par  . Évalué à 6.

          Moi aussi j'ai ete surpris par ce message d'erreur, mais je pense que c'est plus une question de finition qu'autre chose, d'ailleurs le message entre parentheses est moins barbare: "(expected &'static str but found integral variable)". En fait il detecte bien que c'est un entier.

          • [^] # Re: Quel type

            Posté par  . Évalué à 2.

            C’est vrai que c’est pas le mot d’erreur le plus sympa, il y a d’autres messages d’erreurs (par exemple ceux concernant les match) qui sont beaucoup mieux foutu. Bref, je plussoie: manque de finition (le message reste quand même compréhensible et permet de comprendre le problème rapidement, ce qui est quand même l’essentiel, le reste c’est de l’amélioration).

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

        • [^] # Re: Quel type

          Posté par  . Évalué à 3.

          Il le détecte bien, c’est juste présenté de manière un peu « brute » : le I dans <VI0> doit vouloir dire “integral” puisque ça devient <VF0> si on remplace -18 par -18.0.

        • [^] # Re: Quel type

          Posté par  . Évalué à 4.

          Le compilateur a bien détecté que c'est un entier, c'est d'ailleurs bien précisé dans la seconde partie du message. Mais il n'a pas le nom du type exact car ça pourrait être n'importe quel des divers types d'entiers (int, uint, i32, …)

          Si tu ajoute la précision du type :

          let x = if est_majeur { "+18" } else { -18i };
          

          il t'indique bien que tu as un int

  • # De la complexité de la gestion de la mémoire et d'autres

    Posté par  . Évalué à 10.

    J'ai beaucoup aimé cet article, je trouve qu'il présente bien les fonctionnalité de ce langage qui semble encore plus intéressant à chaque version. Maintenant qu'il commence à se stabiliser, je pense que je vais finir par m'y mettre.

    La gestion de la mémoire semble compliqué au premier abord mais j'aimerais ajouter pour ceux qui pense que c'est une hérésie que c'est aussi la voie vers laquelle le C++ s'oriente avec C++11 et C++14. Pour paraphraser Herb Sutter: "dans le futur, aucun programmeur C++ ne devrait utiliser le type pointeur, mais uniquement utiliser unique_ptr, shared_ptr, weak_ptr, …" (http://herbsutter.com/elements-of-modern-c-style/). Certes le C++ est un langage très (trop ?) compliqué mais pour moi ceci prouve que la thématique de la mémoire est loin d'être réglée. On peut invoquer OCaml ou Java comme "success-story" pour le ramasse miette mais la réalité montre que que ce n'est pas si simple, même si cela marche très bien dans beaucoup de cas. D'où l'intérêt d'avoir une gestion intermédiaire, qui optimise statiquement le plus de cas possible. D'ailleurs, une partie de la recherche actuelle dans les langages de programmation porte sur une gestion encore plus fine des pointeurs (http://protz.github.io/mezzo/). D'ailleurs il me semble que la gestion de la mémoire par Rust n'est pas si éloignée de la logique linéaire, comme cela a été noté plus haut.

    J'aimerais aussi ajouter un point sur la partie "objet": j'ai lu certains commentaires de gens qui trouve le mélange objet/fonctionnel un peu étrange. Je trouve au contraire que le compromis atteint est très bon: les fameuses 15 dernières années de recherche en langages de programmation ont montré que concevoir un langage de programmation objet correct est extrêmement difficile, d'ailleurs la plupart des langage objets n'ont pas une sémantique claire. Il a fallut atteindre 2012 pour que quelqu'un démontre que le C++ avait une sémantique correcte (http://herbsutter.com/elements-of-modern-c-style/) et encore, cette étude a montré que la spécification est vague, voir contradictoire, et dans de nombreux cas complètement contre-intuitive, et elle a même menée à des corrections de la spécification. Ceci semble indiquer que l'approche "C++" ou "Java" des objets n'est pas la bonne: elle semble sympathique mais engendre énormément de problèmes, principalement par son manque de sécurité. L'approche des "traits" me semble plus intéressante: elle conserve la partie intéressante des objets, à savoir l'interface, elle donne la possibilité à un objet d'avoir plusieurs interfaces, et surtout elle dissocie complètement l'objet et l'implémentation de son interface. D'où une question intéressante: est-ce que Rust possède une sémantique correcte ?

    Enfin il est vraiment très appréciable de retrouver de nombreux aspects des langages fonctionnels, comme les lambda fonctions, les types sommes, car ce sont des notions très puissantes que l'on comprend bien. La gestion des erreurs par les "conditions" est aussi intéressante, lorsque utilisée à bon escient.

    • [^] # Re: De la complexité de la gestion de la mémoire et d'autres

      Posté par  (site web personnel) . Évalué à 4.

      La couche objet de OCaml a une sémantique correcte, c'était d'ailleurs l'objectif de la thèse de Jérôme Vouillon.

      Un des points intéressant, de ce que j'en ai compris est qu'il ne faut pas confondre classe héritante et sous type, sans quoi on va droit dans le mur. On peut avoir sous classe et sous type en même temps si et seulement si l'ensemble des types de la sous classe sont des sous types de la classe mère.

      « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

      • [^] # Re: De la complexité de la gestion de la mémoire et d'autres

        Posté par  . Évalué à 2.

        Exactement, c'est bien le problème de la plupart des langage actuels: héritage "implique" sous-typage, d'où aucune sémantique ou une sémantique contre-intuitive. Si je ne dis pas de bêtise, cela marche en OCaml car sous-typage et héritage sont découplés: le sous-typage est structurel et pas nominatif, l'héritage permet simplement de réutiliser du code.
        Merci pour le commentaire et le lien :p

    • [^] # Re: De la complexité de la gestion de la mémoire et d'autres

      Posté par  . Évalué à 4.

      est-ce que Rust possède une sémantique correcte ?

      Qu'entends-tu par «correcte»?

      Enfin il est vraiment très appréciable de retrouver de nombreux aspects des langages fonctionnels, comme les lambda fonctions, les types sommes, car ce sont des notions très puissantes que l'on comprend bien.

      Je dirais même que ça permet de faire le pont entre langages objets et fonctionnels. Et vu les fonctionnalités piochées des langages fonctionnels, ça va donne vachement plus envie d'en faire que n'importe quel discours sur «c'est plus facilement parallélisable».

      La gestion des erreurs par les "conditions" est aussi intéressante, lorsque utilisée à bon escient.

      Perso c'est un des seuls trucs que j'ai vraiment pas compris en Rust, et vu que c'est une notion assez avancé je n'en ai pas parlé.

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

      • [^] # Re: De la complexité de la gestion de la mémoire et d'autres

        Posté par  . Évalué à 1.

        Effectivement, je n'aurai pas du utiliser correct: disons une sémantique qui garantie un minimum de sécurité, c'est à dire que la sémantique formelle correspond à l'intuition. Lorsque ce n'est pas le cas (comme en C++ ou autres), on peut écrire du code qui a du sens (probablement), mais pas forcément le sens auquel on s'attend, d'où un bug potentiel.

        Si j'ai bien compris, les "conditions" de Rust, fonctionnent comme une sorte de callback: on définit une condition d'erreur C, et lorsqu'une fonction X rencontre une erreur, elle appelle C en donnant le contexte de l'erreur. Ailleurs dans la pile d'appel de X, une autre fonction va recevoir le message de C (ou bien échouer lamentablement si aucune fonction n'a préciser qu'elle voulait intercepter C) et va pouvoir faire deux choses: exécuter du code (par exemple ajouter un message d'erreur à une liste) et renvoyer une valeur par défaut/sûre/témoins. Le code va retourner à X qui va recevoir de la part C la valeur en question et continuer. La doc est très bien faites donc je ne vais pas répéter l'exemple. Mais informellement, la valeur retournée par C va indiquer à X l'action qu'il doit effectuer: par exemple abandonner le traitement, ou poursuite avec une valeur "sentinelle" (-1), ignore cette tâche (une ligne d'un fichier), etc. Mais le point le plus important, c'est que ce n'est pas X qui va choisir l'action (il va seulement l'appliquer), c'est une fonction quelque part dans la pile d'appel, ce qui permet de gérer les erreurs de façon différentes en fonction du contexte. Cela me paraît très souple lorsque c'est bien utilisé, par contre attention à ne pas générer du code incompréhensible !

        • [^] # Re: De la complexité de la gestion de la mémoire et d'autres

          Posté par  . Évalué à 3.

          Effectivement, je n'aurai pas du utiliser correct: disons une sémantique qui garantie un minimum de sécurité, c'est à dire que la sémantique formelle correspond à l'intuition. Lorsque ce n'est pas le cas (comme en C++ ou autres), on peut écrire du code qui a du sens (probablement), mais pas forcément le sens auquel on s'attend, d'où un bug potentiel.

          En fait c’est très subjectif! ^^

          Tu ne peux pas mélanger les types, tu ne peux pas mélanger les pointeurs, etc. Déjà au niveau du système de type, tout est carré, je vois vraiment pas comment tu peux foirer ton coup. D’ailleurs, il va même plus loin avec un sens contraire à l’intuition pour t’empêcher de faire de grosses conneries (je pense notamment à l’immutabilité par défaut).

          Ensuite on a le match qui sera forcément exhaustif (comme on s’attend à avoir un default dans un switch), les variables qu’on passe de tâches en tâches (bon j’avoue pas forcément intuitif), les boites qui empêchent la majeure partie des types de fuites de mémoire, impossible d’écrire du code non-sûr sauf dans un bloc spécifique, presque impossible de faire des erreurs de segmentation, un maximum de truc vérifiés à la compilation (donc grâce à la syntaxe), etc.

          Et puis la syntaxe familière à tout ceux qui ont fait du C (c’est à dire à peu près tout le monde), donc même s’il faut piger quelques trucs spécifiques au langage/aux langages fonctionnels, je vois mal comment on peut s’y prendre pour faire des conneries.

          Si j'ai bien compris, les "conditions" de Rust, fonctionnent comme une sorte de callback: on définit une condition d'erreur C, et lorsqu'une fonction X rencontre une erreur, elle appelle C en donnant le contexte de l'erreur. Ailleurs dans la pile d'appel de X, une autre fonction va recevoir le message de C (ou bien échouer lamentablement si aucune fonction n'a préciser qu'elle voulait intercepter C) et va pouvoir faire deux choses: exécuter du code (par exemple ajouter un message d'erreur à une liste) et renvoyer une valeur par défaut/sûre/témoins. Le code va retourner à X qui va recevoir de la part C la valeur en question et continuer. La doc est très bien faites donc je ne vais pas répéter l'exemple. Mais informellement, la valeur retournée par C va indiquer à X l'action qu'il doit effectuer: par exemple abandonner le traitement, ou poursuite avec une valeur "sentinelle" (-1), ignore cette tâche (une ligne d'un fichier), etc. Mais le point le plus important, c'est que ce n'est pas X qui va choisir l'action (il va seulement l'appliquer), c'est une fonction quelque part dans la pile d'appel, ce qui permet de gérer les erreurs de façon différentes en fonction du contexte. Cela me paraît très souple lorsque c'est bien utilisé, par contre attention à ne pas générer du code incompréhensible !

          Ça me fait un peu penser aux Exceptions (sans les problèmes de sûreté) mais la syntaxe est quand même assez différente et… perturbante. Ça me parait plus souple que les exceptions, mais à voir à l’utilisation.

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

          • [^] # Re: De la complexité de la gestion de la mémoire et d'autres

            Posté par  (site web personnel) . Évalué à 4.

            Ça me fait un peu penser aux Exceptions (sans les problèmes de sûreté) mais la syntaxe est quand même assez différente et… perturbante. Ça me parait plus souple que les exceptions, mais à voir à l’utilisation.

            Si ça ressemble visuellement aux Exceptions, la sémantique est assez différente, puisqu'il manque la fonctionnalité la plus puissante (mais aussi la plus controversée), à savoir la remontée au travers de la pile (et donc des appels de fonctions). En effet dans Rust, le gestionnaire de conditions est exécuté à l'endroit du raise, et ne constitue pas une autre façon de sortir de la fonction.

            Bien utilisée, la remontée dans la pile permet d'écrire du code court et d'éviter pas mal de code boilerplate. Malheureusement ce comportement casse la sémantique de Rust, qui ne pourrait plus fournir ses garanties.

            Je suis assez sceptique sur l'utilité des conditions, parce que si le discours est qu'on peut coder le comportement que l'on veut face à une erreur, j'ai l'impression qu'il faudrait plus ou moins penser à tous les comportements possibles, afin que la signature du handler permette dans les faits de gérer l'erreur comme on le veut. Je me dis que dans la pratique on va peut-être la plupart du temps finir par gérer l'erreur d'une manière figée et retourner un Option/Result.

            Mais j'espère me tromper, et je vais faire confiance aux développeurs de Rust qui ont fait du super boulot pour le reste, donc y a pas de raisons ! Et Rust possède à côté d'autres outils permettant d'éviter le boilerplate.

      • [^] # Re: De la complexité de la gestion de la mémoire et d'autres

        Posté par  . Évalué à 5.

        est-ce que Rust possède une sémantique correcte ?

        Qu'entends-tu par «correcte»?

        Elle se tient bien à table et éviter de crasher sur les $convives.

        En général, seules les sémantiques qui sont passées par une école de maintien sont correctes. Celles issues d'un milieu défavorisé sont revêches et peu fiables.

    • [^] # Re: De la complexité de la gestion de la mémoire et d'autres

      Posté par  . Évalué à 4.

      Le ramasse-miette suffit dans beaucoup de cas, mais il apporte d'autres problemes tels que: avec un ramasse miette "stop-the-world", il est impossible de faire du temps reel ou de garantir un temps de reponse.

      Et c'est un probleme qui grossit avec la taille de la memoire. Java a ces ennuis. Le ramasse-miette G1 etait sense apporter des solutions a ces problemes, mais ces promesses tardent a se materialiser. Plus recemment, j'ai vu la JVM Azul qui permet un passage a l'echelle du ramasse miette sur plusieus dizaines voire centaines de Go de memoire. Le mecanisme utilise (invalidation de pages memoire et mise a jour des references lors d'acces memoire vers les page invalidees) etant un brin avant gardiste, linux ne sais pas bien le gerer et ils ont cree un patch noyau hyper invasif qui ne sera jamais integre upstream tel quel. Il y a bien sur un gros surcout memoire pour cette fonctionnalite.

      Le probleme du ramasse-miette dans Java et lie au fait que n'importe quel thread puisse voir absolumment toute la memoire du processus. Comme indique dans la depeche, Rust contourne ce probleme en:
      1- fournissant et encourageant l'utilisation par defaut d'autres mecanisme de gestion de la memoire que le seul ramasse-miette
      2- faisant en sorte que chaque thread ne puisse voir qu'une portion de la memoire qui lui est dediee, et pas celle des autres threads. D'ou l'absence du "stop-the-world". J'imagine qu'un cas pathologique pourrait etre equivalent a un stop the world (programme mono thread, uniquement code avec le ramasse-miette), mais je pense que les mecanismes mis en place devraient empecher pas mal de programmes d'en arriver la.

  • # Fedora

    Posté par  . Évalué à 3.

    Pour les utilisateurs de Fedora, il est possible d'essayer Rust sans trop de larmes grâce aux Coprs, l'équivalent des PPA chez Ubuntu.

  • # super journal

    Posté par  . Évalué à 3.

    bravo pour le journal claire et extremement bien détaillé. Ca me donne envie de me mettre au Rust!

Suivre le flux des commentaires

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