Langage rust

Rust, langage de programmation pour luddites

Conçu par Mozilla pour écrire Servo, éventuel successeur à Firefox, il utilise LLVM en backend... et un hard codeur en frontend.

Le code de Rust est disponible sur Github, le langage intéresse des programmeurs qui y voient un successeur possible à C++ dans leur domaine.

Le mot "rust" signifie roux en anglais ancien et peut être une allusion au renard, symbole de Mozilla. C'est aussi le nom d'un champignon (on pense à hallucinogène).

Rust n'est pas un nouveau C auquel on aurait ajouté des tableaux dynamiques, des objets, un pattern matching mixant des types différents, une gestion automatique de la mémoire, comme l'a fait Vala.
Son principal avantage est la sécurité de la gestion mémoire, il reprend par ailleurs de vieux principes avec de nouveaux mots-réservés et une syntaxe qui est sans doute plus expressive, quand on parvient seulement à la lire. Il semble essayer d'être différent pour le principe et non pour l'efficacité.

Voici l'opinion de l'auteur de "La cathédrale et le bazar", Eric Raymond au sujet de Rust:

Même les choses les plus simplistes comme la concaténation de deux chaînes de caractères sont déraisonnablement difficiles. Le langage demande une grande quantité de rituel flou et obscur avant que vous puissiez obtenir quoi que ce soit.

Il est clair que les créateurs du langage sont très éloignés de la ludification du travail, et tendent plutôt à la propension luddite. On préférerait que la sécurité soit obtenue par un backend plus évolué (voir Erlang) que par un travail accru dans le codage.

Comment le compilateur fonctionne

Les étapes de génération d'un programme exécutable sont décrites dans le schéma ci-dessous:

Le scanner puis le parseur produisent un code AST (Abstract Syntax Tree). Des optimisations sont effectuées sur ce code.
Puis une analyse sémantique est réalisée qui effectue de multiples contrôles. La version 2016 du compilateur le fait en deux étapes: création d'un code intermédiaire de haut niveau puis de niveau moyen.
L'étape suivante est la génération de code LLVM, qui se fait à partir de la MIR et en combinaison avec l'API LLVM. On a le choix entre produire du bitcode, ou de l'assembleur ou du code objet. Dans le dernier cas un l'éditeur de liens de LLVM produit un code exécutable propre à la plateforme.

Syntaxe du langage

Comparons les éléments de base de Rust, avec ceux de C ou C++ ou d'un langage modernecomportant ces nouveaux éléments que l'on trouve dans tous les langages récents.

Afficher dans la console.

Rust

fn main() {      
  println!("Hello World!");  
}

C

void main() {      
  puts("Hello World!");  
}
 
Afficher une 'variable' chaîne.

Rust

let str = "pourquoi ne pas faire compliqué";
println!("str: {}", str);

C

char *str = "quand on pourrait faire simple";
puts(str);
Quand le type String est spécifié, pour assigner une chaîne de caractères il faut préciser que c'est une chaîne!

Rust

let alpha: String = "Beta".to_string();

C

char *alpha = "Beta"
 
En Rust, toutes les variables locales sont déclarées par let et sont des constantes! Il faut ajouter le déclarateur mut, pour pouvoir les assigner de nouveau...

Variable par défaut en Rust

let a = 1;
a += 1;   // Erreur! 

Constante C

const int a = 1;
a += 1;   // Erreur! 
 
Une 'variable mutable' en Rust est une variable (comme le nom d'indique) en C!!!

Variable mutable Rust

let mut b = 2;
b += 1;  // Ok

Variable C

int b = 2;
b += 1;  // Ok
 
Le type est déterminé par inférence, mais comme cela peut être ambigu (et donc non sûr, il faut le noter), on a ajouté la possibilité d'une déclaration explicite en option. Le caractère coercitif du langage aurait voulu que ce soit la syntaxe par défaut.

Types explicites Rust

let b : int = 2;

let ch : char = 'x';

Types explicites C

int b = 2;

char ch = 'x';
 
Les tuples sont des listes mixtes. Un tableau à le même usage.

Tuple en Rust

let t = (4, 5.0, false, "hello");

Tableau mixte en langage moderne

array t = [ 4, 5.0, false, "hello"]
 
La structure switch est renommée match pour faire plus neuf. L'essentiel est qu'elle est plus élaborée, comme en Scala.

Match en Rust

let x : int = 1;
match x {
  0 => { println!("Non trouvé"); } 
  1 => { println!("Trouvé"); } 
  _ => { println!("Défaut"); } // par défaut
}

Switch case en C

int x = 1;
switch(x) {
  case 0: puts("Non trouvé"); break;
  case 1: puts("Trouvé"); break;
  default:
          println!("Par défaut"); 
}
 
La boucle for inspirée de Python est le seul cas ou Rust est plus simple que C. Les premières versions du langage écrivaient: for i in range(0, 10). Puis on a découvert que la syntaxe du langage Scriptol était la meilleure.

Rust

for i in 0..10 {
  ...
}

C

for(i = 0; i <= 10; i++) {
  ...
}
 
Rust utilise une structure spéciale pour les boucles sans limite définie. Pas indispensable.

Rust

let ctr = 0;
loop {
  ctr += 1;
  if(ctr == 1) {
    break;  
  }
}

C

#define forever 1
int ctr = 0;
while(forever) {
  ctr += 1;
  if(ctr == 1) {
    break;  
  }
}
 
La déclaration d'une fonction montre bien l'aspect verlan de Rust par rapport à C! En-tête plus compliquée sans raison, retour plus simple mais inutilement moins sûr, ou est la cohérence?

Rust

fn mult(x: int, y: int) -> int {
  x * y
}

C

int mult(int x, int y) {
  return (x * y);
}

Lecture des lignes d'un fichier une à une...

Rust

let filename = "myfile.txt";
let f = try!(File::open(filename));
let mut file = BufReader::new(&f);
for x in file.lines() {
    let line = x.unwrap();
    println!("{}", line); 
}

C++

char *filename = "myfile.txt";
string line;
ifstream file(filename);
while(getline(file, line)){
   cout << line << endl;
}
 
Les struct servent de classes mais les méthodes sont déclarées séparément, ce qui est une option en C++.

Struct en Rust

struct Point {
    x: int,
    y: int,
}

impl Point {
    fn Start() -> Point {
        Point { x: 0, y: 0 }
    }
}

Class en C++

class Point {
    int x;
    int y;
    Point Start() {
       x = 0; 
       y = 0;
       return this;
    }
}

 

En conclusion, Rust a été conçu comme langage système pour remplacer C++ avec une gestion mémoire plus sûre, un point qu'on ne lui disputera pas. Etait-il nécessaire pour parvenir à ce résultat de changer la syntaxe et la rendre inutilement obscure? Les motivations des auteurs ne sont pas claires, car les défauts de C, à savoir les points-virgules (une erreur selon les auteurs de C eux-mêmes) sont conservés, mais la simplicité des déclarations ne l'est pas.
Les possibilités des éléments du langage ont aussi été restreintes pour réduire le risque d'erreurs. C'est quelque chose qui n'a pas réussi dans le passé à Pascal ou Modula.

Faut-il utiliser Rust?

Faut-il utiliser Rust, donc le choisir plutôt que Go ou C++? On mettra go de coté car il est plutôt en compétition avec les langages de serveurs comme Java, Python, PHP, un garbage collector peut être un inconvénienr, et son système de modules en ligne l'est encore plus, ainsi que l'absence de généricité.
Reste donc à choisir entre l'insécurité de C++ quand il est mal programmé, mais avec une liberté totale et la possibilité de réaliser ce que l'on veut, ou faire avec la syntaxe obscure de Rust pour une gestion mémoire plus sûre. Il y a de nombreuses raisons de rester en C++: des tonnes de bibliothèques, tous les outils de développements utiles, la vitesse d'exécution.

Référence : Manuel de référence Rust (Anglais).

Projets similaires: Cyclone (ATT), Checked C (Microsoft).

Langages de programmation et de données Asm.js - Basic - C - C++ - C# - Dart - Eiffel - Go - Java - JavaScript - Julia - Pascal - PHP - Python - Prolog - Ruby - Scala - Scriptol - Swift - TypeScript - HTML - Wasm - XML - XAML - SQL