Ca n’est pas le Chtirug, mais ça pourrait, puisqu’on va faire du Rust ce soir. Et c’est cool, parce que le Rust, j’aime bien !
François est déja venu à Lille pour le devfest (et c’était bien).
C’est quoi Rust ?
Pour François c’est un langage système rapide qui évite les SEGFAULT et qui facilite la thread safety. Et l’intérêt pour François, c’est l’exécution sûre, la performance et l’expressivité du langage.
La magie vient de trois éléments
- Ownership
- Borrowing
- Lifetime
Et François va tout faire pour les éviter (je le comprend, ce sont des concepts loin d’être évidents pour les débutants … qui ne présentent de l’intérêt que quand on code un peu salement). D’ailleurs François (comme moi, dans une moindre mesure) essaye régulièrement de proposer aux gens de développer en Rust.
Curieusement, quand François a commencé a faire du Rust, le compilateur passait son temps à lui dire « non ». Donc Rust a des killer features. En plus, Avec Rust, « it compiles, it run ». Et la doc de Rust est canon (le mot est faible, en fait).
Malheureusement, Rust est présenté comme un langage système … ce dont tout le monde se cogne (sauf Clever Cloud). Et pour faire du web, ça paraît inapproprié … Pourtant, c’est ce qui est arrivé à go : c’est un langage pensé pour remplacer C … et maintenant on s’en sert pour remplacer Java. Et François veut qu’on considère Rust de la même manière.
Le sujet
A Montpellier, pour faire gagner les places, ils ont une lotterie : ils récupèrent la liste des spectateurs dans EventBrite, et tirent au sort « n » gagnants.
Et François va donc nous montrer la v3 de cette application. Et c’est cool, parce que du coup, le code est sur GitHub !
On commence par Cargo, qui est l’outil de build Rust. Cargo existe dès le début de Rust (à la différence de Go … par exemple).
Appeler EventBrite
Classiquement, François utiliser reqwest pour faire les appels HTTP, et Failure, pour avoir de beaux messages d’erreur (parce que par défaut, on n’a à peu près que panic!
). Ah, et Serde, bien sûr, pour (dé)sérialiser.
L’avantage de reqwest, c’est qu’avec une fonction, on fait des HTTP GET. C’est un peu mieux qu’en Java, encore aujourd’hui …reqwest::get
retourne un Result
, qui peut donc être Ok
ou Error
. En tapant reqwest::get(…)?
, si le résultat est un Error
, on sort tout de suite de la fonction (au lieu d’avoir à vérifier à chaque ligne qu’on a une erreur … un peu comme en go). Il faut en fait retenir que si votre fonction peut retourner des erreurs, elle doit retourner un Result
.
De la même manière, il n’y a pas de null
en Rust. Du coup, comme dans d’autres langages, on retourne une Option
.
Pour un langage qui est réputé être bas-niveau, lire une liste de spectateurs paginée est assez facile, grâce aux éléments fonctionnels (comme le map(…)
et le flatMap(…)
).
Evidement, on peut lire les variables d’environnement avec env::var(…)
qui retourne évidement un Result
pour le cas où la variable n’existe pas. Et quand elle n’existe pas, on peut générer facilement une erreur avec Result.expect("le message d’erreur")
.
Une fois qu’on a récupéré tout ça, il peut être utile de transcoder avec From
et Into
Tirer au sort les gagnants
Pour tirer au sort les gagnants, on fait un petit coup de pattern matching, qui marche assez facilement en Rust.
Par contre, pour récupérer un Vec
avec le bon lifetime, François nous fait un bout de code un peu sale. Parce que clone()
, c’est sale au sens où on copie le contenu de la mémoire d’un endroit à un autre. Et c’est le genre d’endroit où le côté bas-niveau de Rust pique : Java fait ça souvent, mais on ne le voit jamais.
Exposer ce résultat en HTTP
François le fait avec actix-web, qui fournit une API web … et des acteurs. Un bon point pour actix-web : la création d’erreurs HTTP à partir d’erreurs métier est assez intuitive. Et en bonus, évidement, tout le code métier peut être appelé de façon asynchrone facilement.
C’est conceptuellement très bien, mais je ne suis pas fan d’actix-web : la définition des routes n’est pas super lisible, et les contraintes liées au démarrage du système d’acteurs ne sont pas super chouettes. J’aurais tendance à préférer rocket, par exemple … (juste parce que comme en Java, il utilise des annotations pour définir les routes, et que je trouve ça cool).
Ajouter un cache
Là, c’est facile grâce à actix-web : il suffit d’ajouter un acteur de cache, et le connecter à notre API.
Bon, j’ai vu quelques &&
qui sont assez mauvais signes en termes de gestion mémoire, mais rien d’insurmontable dans de l’informatique web. Autrement dit, c’aurait été gênant pour un driver ou un OS, mais pour une appli web qui lit EventBrite, franchement, ça n’est pas bien grave.
Et avec le cache, ça va quand même sacrément vite !
Mettre à jour le cache
Pour ça, il suffit d’utiliser Tokio (parce qu’actix-web se base sur Tokio). Et avec Tokio, on peut demander à Rust de lancer, en asynchrone, un bout de code qui s’exécutera toutes les 5 secondes pour rafraîchir le cache. Curieusement, François utilise le mot clé move
, qui ne fait plus partie du langage.
Conteneuriser
Evidement, mettre du Rust dans un conteneur, c’est facile, parce que l’exécutable peut se compiler comme go sans dépendance système. Par contre, comme François embarque une base sqlite et libssl, il lui faut des dépendances système … et pour se faciliter la vie, il prend celles d’Ubuntu.
Le mot de la fin de François
En une heure, il nous a montré une application facile qui expose néanmoins les problèmes classiques des applciations web
- Une api HTTP
- Une gestion d’état
- Un client HTTP
- Une gestion des erreurs avancée
Vous n’allez pas tout recoder en Rust, parce que ça n’est pas facile. Mais vous allez apprendre beaucoup, notament sur la gestion de la mémoire. Et curieusement, pour une appli web simple, vous n’avez pas besoin de vous intéresser de trop au borrow checker et aux lifetimes.
Pour le dire autrement, ce que vous faites en Go, vous pouvez le faire en Rust, avec de meilleures garanties au runtime (de qualité de code, de sécurité).
Conclusion
J’étais conquis par Rust avant la présentation, donc c’était facile. Je retire néanmoins plusieurs choses de cette présentation. D’abord, que faire des .clone()
, ça n’est pas si dramatique. C’est évidement moins bien que de s’en passer, mais ça n’est pas la fin du monde. Ensuite, que malgré l’aspect un peu rébarbatif de la compilation qui ne marche jamais du premier coup, on peut faire des applis web en Rust qui dépotent. Enfin, et c’est le plus important, Rust va continuer à grandir parce qu’il offre une bien plus grande garantie à l’exécution que d’autres langages. Et ça, c’est à mon avis le plus important, puisque c’est ce qui permet de limiter les frais de maintenance logicielle (typiquement, quand on écrit du Rust, si ça compile, ça marche). Entre ça et l’utilisation possible de Rust dans AWS Lambda, j’ai l’impression qu’il commence à se passer quelque chose d’intéressant …