Pas si dur, le Rust !

introduction (je raconte ma vie)

J'ai commencé à m'intéresser à l'informatique vers l'âge de 15 ans. Je ne sais pas ce qui m'a guidé vers le langage C, mais le "Site du Zéro" avait l'air de dire que c'était un bon langage pour commencer, alors je m'y suis mis. C'est avec ce langage que j'ai commencé à découvrir la programmation, le fonctionnement d'un ordinateur, les interfaces graphiques... Je ne suis pas allé très loin, mais ces bases m'ont servi de porte d'entrée vers le logiciel libre et beaucoup de sujets qui me passionnent aujourd'hui. À l'époque, je ne connaissais que windows et Code::Blocks. J'avais deux amis qui s'intéressaient également à la programmation, l'un faisait du Java, l'autre du C#. Déjà à l'époque ces deux langages ne m'intéressaient pas du tout. C'est également à cet âge que j'ai découvert Blender et la modélisation 3D.

Pendant la prépa, j'avançais à vitesse réduite. Il y avait suffisamment à faire avec les maths et la physique pour ne pas me rajouter une charge de travail supplémentaire. Mais ma curiosité était toujours là, et je picorais quelques notions sur LaTeX, Ubuntu, GNU/Linux, le web, python... sans jamais m'y mettre. C'est au moment des concours, en avril 2014, que les choses se sont accélérées : tous les soirs en rentrant des épreuves écrites, je me mettais au HTML5 et CSS3. Comme WAMP ne marchait pas bien, j'ai décidé d'installer Ubuntu (et XAMPP sur ma machine, et par erreur j'ai écrasé mon système Windows, ce qui m'a fait perdre toutes mes fiches de révisions en LaTeX juste au moment où je n'en avais plus besoin, heureusement. J'avais déjà connaissance de django, mais n'ayant jamais vraiment fait de python, ça m'avait paru hors de portée, et je me mettais donc au PHP et SQL.

En arrivant à SupOptique, je trouvais qu'il manquait un club d'informatique, alors je l'ai créé. La première mission que je lui fixais était de faire un site des étudiants auto-hébergé (l'idée de le faire héberger par quelqu'un d'autre ne m'avait même pas traversé l'esprit !). Après des tentatives peu fructueuses pour lancer un site fait à la main, j'ai jugé qu'un wordpress serait plus facile à transmettre aux générations d'après. Et visiblement, j'ai eu raison, parce que le site existe toujours et semble toujours (relativement) animé. Il y a sujet à dire beaucoup d'autres choses, mais j'aimerais vous éviter d'avoir à trop scroller avant de découvrir le Rust ! Il faut juste savoir que les cours de C à Supoptique était nuls et que j'y ai appris le Matlab.

(TODO: parler de ce qui s'est passé entre temps, [donnéespersonnelle, microcontrolleur, Debian, Wordpress, git, django, FPGA, RedPitaya, bash, python, Manjaro, autohébergement, Matlab, C#, API, Julia, Websocket])

Hello World!

Faisons un bon dans le temps vers aujourd'hui, en 2019, époque à laquelle je code mon premier "Hello World!" en Rust. J'ai pour la première fois entendu parler du Rust grâce à Eloïs, au festival passage en seine 2018 et lu le RustBook pendant l'été sur mon téléphone. Ça faisait un moment que je souhaitais contribuer à Durs (duniter-rs) mais le pas du Rust me semblait trop difficile à franchir. C'est grâce aux similarités avec le langage Julia, que j'ai découvert il y a peu de temps que j'ai eu le courage de m'y mettre. Voici mes premières impressions (désolé pour les imprécisions).

  1. installation extrêmement simple
  2. cargo, gestionnaire de paquet à la pip
  3. compilateur gentil qui donne des conseils
  4. excellent support pour la documentation
  5. crates bien documentées faciles à utiliser
  6. langage succinct, forme canonique satisfaisante
  7. confiance dans le programme une fois compilé

Mon programme est un simple jeu de "plus ou moins", le premier exercice que proposent les tutoriels, mais ma version a la particularité d'avoir une interface web et de communiquer par websocket. Je pose mon code ici (également disponible sur Gitlab) et je l'explique ensuite.

back (rust)

extern crate ws; // WebSocket crate

use rand::Rng;
use std::cmp::Ordering; // ordering // random

fn main() {
    // generate random number
    let secret_number: u32 = rand::thread_rng().gen_range(0, 10);

    // run websocket server
    let _srv = ws::listen("127.0.0.1:3012", |out| {
        // closure passed to websocket listen function
        move |guess: ws::Message| {
            // defines a response
            let response: &str = match guess.into_text() {
                // convert input into text
                Ok(guess) => match guess.trim().parse::<u32>() {
                    // convert text into number
                    Ok(guess) => match guess.cmp(&secret_number) {
                        // compare number to generated
                        Ordering::Less => "Too low",
                        Ordering::Greater => "Too high",
                        Ordering::Equal => "you found !",
                    },
                    Err(_) => "not valid",
                },
                Err(_) => "not text",
            };
            out.send(response) // send response to websocket
        }
    });
}

front (vuejs)

<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
  <title>Guess through websocket</title>
  <style>
    .response {
      color: blue;
    }
  </style>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
<h1>WebSocket guess Rust application</h1>

<!-- vuejs app template -->
<div id="app">
<span> Please enter a number between 0 and 10 in this field and press 'enter' </span> <br>
<input type="text" v-model="text" v-on:keyup.enter="send" style="width:40em"/> <br>
<span> The server responded: </span> <span class="response"> {{ response }} </span>
</div>

<!-- vuejs app -->
<script>
var app = new Vue({
  el: '#app',
  data: {
    text: undefined,
    response: "enter a number",
    socket: undefined,
  },
  computed: {
  },
  methods: {
    send: function () {
      this.socket.send(this.text);
    },
    openSocket: function () { // set websocket
      this.socket = new WebSocket("ws://127.0.0.1:3012/");
      this.socket.onmessage = function (event) {
        app.response = event.data // store message it in response
      }
    }
  },
})

app.openSocket(); // initialize connection

</script>

</body>
</html>

explications

génération d'un entier entre 0 et 10

let secret_number: u32 = rand::thread_rng().gen_range(0, 10);

démarrage d'un serveur websocket. le deuxième argument est une 'closure', soit une sorte de fonction anonyme.

ws::listen("127.0.0.1:3012", |out| { move |guess| { [...] ; out.send() } });

suite d'opérations retournant un Result qui vaut soit Ok soit Err avec gestion extensive des cas.

... into_text() ... parse::<u32>() ... cmp() ...

Et pour le front : ouverture d'un nouveau websocket

socket = new WebSocket("ws://127.0.0.1:3012/");

définition de la fonction traitant la réception de message

socket.onmessage = function (event) { ... event.data }

envoi d'un message sur le socket

socket.send(message);

commentaires

  • Inclusion similaires au #include en C, aux import en python ou aux use en Julia, rien de particulier.
  • Annotations de type à peu près comme en Julia.
  • Démarrage d'un serveur websocket en une ligne.
  • Gestion des cas extensive à l'aide de la syntaxe match.

conclusion

En commençant cet exercice, je pensais que ça allait me prendre du temps, qu'il allait falloir lire beaucoup de documentation, que je me lançais dans quelque chose d'un peu dur d'un seul coup, que ça allait être difficile à débugger... bref comme d'habitude quand on commence un langage. Sauf que pas du tout, j'ai juste dialogué avec le compilateur jusqu'à ce qu'il accepte de compiler mon programme et ça c'est mis à marcher.

Ça me donne envie de faire plus de Rust, il faut juste que je trouve le temps pour ça !