Files
BaC/content/2019-10-24_RUST-:-jouer-avec-le-typage-fort.md
2025-02-27 12:52:48 +01:00

110 lines
5.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

+++
title = "Rust : jouer avec le typage fort"
date = 2019-10-24
aliases = [ "post/2019/04/24/RUST-:-jouer-avec-le-typage-fort"]
[taxonomies]
tags = [ "codaz", "rust" ]
+++
Pierre qui rouille… attendez, quoi ?!
<!-- more -->
Jai récemment appris Rust. Parce que le langage est marrant avec son concept dappartenance (*ownership*), sa gestion des erreurs très intelligentes, ses pointeurs intelligents (*smart pointers*). Parce que jaime bien apprendre des choses.
Et surtout parce que quand le shell nest plus suffisant, passer en python avait une certain tendance à me casser les noix : il faut des librairies installées ou il faut distribuer de grosses librairies avec certains programmes ou scripts alors quon peut simplement tout embarquer dans un exécutable Rust si on en a besoin.
Bref, cest cool.
Toutefois, Rust reste un langage fortement typé et quand on a lhabitude des langages faiblement typés (PHP, Python, etc…), cela peut nécessiter un sacré temps dadaptation pour sy remettre. Soyons clairs : jaime bien les langages fortement typés. Ça donne limpression de savoir ce que lon fait et ça permet déliminer un nombre substantiels de causes derreur. Mais cest aussi pas évident du tout à manipuler et ça prend nettement plus de temps pour arriver au même résultat quun langage faiblement typé.
![YAY](/.typing_m.png)
Je nai pas la prétention de vous apprendre à programmer en Rust. Même si jai commencé à faire des choses bien plus compliquées dans ce langage, jai débuté comme tout le monde. Et au moment de débuter, on bute toujours sur des choses stupides, comme ce que je vais vous présenter là et qui a, du coup, un rapport avec une particularité du langage, le typage fort.
Si vous êtes débutant en Rust, ou si vous ne connaissez pas Rust et que vous souhaitez lapprendre, ceci pourrait vous intéresser.
## Alors la marmotte, elle met le chocolat dans un entier 64 bits non-signé
Prenons un exemple tout con. On va faire une fonction dont le seul objectif est dafficher un nombre qui est donné en paramètre. Cette fonction pourrait sécrire comme ça :
```rust
fn print_number(a: u64) {
println!("{}", a);
}
```
Tu peux alors lutiliser de manières assez simple :
```rust
fn main() {
let a: u64 = 8;
print_number(a);
}
```
Basiquement, ça va fonctionner, mais cela pose quand même un gros souci : on ne peut passer à cette fonction que des `u64`. Or, `a` dans notre exemple pourrait très bien tenir dans un `u8`. Mais si lon fait, ça, ça ne fonctionnera pas :
```rust
fn main() {
let a: u8 = 8;
print_number(a);
}
error[E0308]: mismatched types
--> src/main.rs:9:18
|
9 | print_number(a);
| ^
| |
| expected u64, found u8
| help: you can convert an `u8` to `u64`: `a.into()`
```
Du point de vue de Rust, cest hyper cohérent : on lui dit quune fonction prend en paramètre un `u64`, on essaie de lui passer un `u8`, le compilateur gueule parce que ce nest pas normal. Ce nest pas le même type. Et même si dans labsolu un `u8` rentre parfaitement dans un `u64`, Rust sen moque : un type est un type et un type « compatible » reste un type différent.
Alors évidemment, tu peux tenter de caster systématiquement ta variable dans un `u64` (cest ce que propose le compilateur en fait). Pour cela, tu pourrais appeler la fonction de cette manière :
```rust
print_number(a as u64);
```
Ça règle effectivement le problème mais :
* ce nest pas forcément très élégant
* tu vas te retrouver à jongler avec des types alors que finalement, ça ne tintéresse pas des masses
Tu pourrais aussi imaginer de faire une fonction pour chaque type : `u64`, `u32`, `u16`, etc… mais franchement, ce serait hyper fastidieux.
Du coup, comment on fait ?
## Tu peux aller timplémenter Martine !!
En fait, on peut très facilement rendre cette fonction beaucoup plus universelle en remplaçant le type fixe par un ''Trait''. Lidée est ici de dire : finalement vu ce que je fais dans la fonction, ce qui mintéresse, cest que ce qui est passé en paramètre puisse safficher dans un `println!` pas tellement le fait que ce soit un nombre.
Et du coup, on peut réécrire cette fonction de la manière suivante :
```rust
fn print_number(a: impl std::fmt::Display) {
println!("{}", a);
}
```
À partir de ce moment, quelque soit ce quon passe dans la fonction (et même pas forcément un nombre !), tant que ce type implémente la fonction `fmt::Display`, ça fonctionnera !
Du coup, tu peux très bien imaginer de faire nawak :
```rust
fn main() {
let a: u8 = 8;
let b: u16 = 16;
let c: f64 = 3.1416;
print_number(a);
print_number(b);
print_number(c);
}
```
## Conclusage
Je voulais juste donner un tout petit exemple sur une problématique qui paraît hyper simple dans un langage faiblement typé, mais qui se révèle bien plus compliqué dès que le langage est fortement typé. Quand on commence à apprendre Rust, cest typiquement le genre de choses sur lequel on bute bêtement au départ parce que ce nest pas vraiment intuitif (en tout cas, pas tant quon a pas lu le chapitre sur le sujet dans le Book).
Quand on vient du C ou du PHP, cest le type de problématique qui paraît insurmontable au départ alors quen réalité, cest tout bête à résoudre. Et je trouve personnellement que Rust a une méthode particulièrement élégante pour le résoudre en prime et évidemment, on peut extrapoler cette méthode pour se donner bien plus de possibilités.