Apprendre le Rust

Le langage multi-paradigme de la fondation Mozilla

a marqué ce sujet comme résolu.

J'ai survolé, et je suis d'accord avec Vayel. Présenté ainsi, ça fait un peu foutra. On passe des erreurs à Cargo sans réel logique.

Pour la question des débutants, pour avoir essayer de toucher à Rust (python étant le seul langage que je connaisse bien, même si je bosse régulièrement en C/C++/Fortran) avec la doc officiel, je ne peux que confirmer ce que dit nohar, c'est abrupte.1

Le lien vers ton blog est cassé.

Et sinon, bravo pour le boulot accompli. ^^ Faire un tuto prend beaucoup de temps, d'autant plus quand il est bien fait (ce qui semble être le cas ici).


  1. À tel point que je reste dubitatif sur la possibilité de ce langage à sortir d'une niche (mais ce n'est pas le bon sujet pour parler de ça). 

+0 -0

Et pourtant, les contributeurs de Rust font de gros efforts pour tenter de le rendre accessible. Je vais en parler avec ceux qui bossent sur le rust-book, ça sera toujours utile.

Du coup, il serait très intéressant que vous lisiez mon tutoriel. Je me demande si les "reproches" adressés au rust-book le sont aussi pour mon tutoriel (dur de se faire une opinion quand on en est l'auteur malheureusement…).

EDIT: j'ai réparé l'url de mon blog (j'avais oublié d'enlever le www).

+0 -0

Ah mais je ne reproche rien au rust-book en lui-même, au contraire je le trouve très bien écrit. Simplement les notions sous-jacentes au langage sont difficilement appréciables par quelqu'un qui n'a jamais eu besoin de choisir explicitement entre une valeur ou une référence, et qui a très bien vécu jusqu'à présent avec un garbage collector et un mécanisme d'exceptions : i.e. très difficilement appréciables pour un dev pur Python, donc.

+1 -0

Rien n'est passé par valeur en Rust, c'est toujours des références. La seule différence est dans le fait que l'on souhaite changer de propriétaire ou non. Exemple:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
struct Foo;

fn lose_ownership(v: Foo) {}
fn dont_lose_ownership(v: &Foo) {}

fn main() {
    let x = Foo;
    // on peut faire ceci:
    dont_lose_ownership(&x);
    lose_ownership(x);

    // mais pas ceci (ne pas oublier de commenter les deux lignes au-dessus pour tester ça):
    lose_ownership(x);
    dont_lose_ownership(&x); // error: use of moved value: `x`
}

Dans le premier cas, pour récupérer la propriété de la variable, il faudra la retourner :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
fn lose_ownership(v: Foo) -> Foo { v }
fn dont_lose_ownership(v: &Foo) {}

fn main() {
    let x = Foo;
    // on peut faire ceci:
    dont_lose_ownership(&x);
    x = lose_ownership(x); // on récupère la propriété

    // mais pas ceci (ne pas oublier de commenter les deux lignes au-dessus pour tester ça):
    x = lose_ownership(x); // on récupère la propriété
    dont_lose_ownership(&x); // donc ici c'est bon
}

Dans tous les cas, ce sont des références et non pas des valeurs (même si c'est caché). Le seul moyen de passer par valeur est d'implémenter le trait Copy sur le type (les types i8, i16, f32, etc… l'implémentent par exemple).

+0 -0

Rien n'est passé par valeur en Rust, c'est toujours des références. La seule différence est dans le fait que l'on souhaite changer de propriétaire ou non.

imperio

Oui ça je l'avais compris.

Je parle plus de la facilité à assimiler ce concept quand on vient d'un langage où littéralement tout est passé par référence et où il n'existe aucune syntaxe pour différencier les références.

Si je prends C++, par exemple, je peux tout à fait choisir de passer une structure par valeur (copie), par pointeur (avec une syntaxe différente) ou par référence (avec la fameuse syntaxe & d'emprunt du Rust) ; contrairement à Python où on ne manipule jamais ce genre de choses (donc on ne soupçonne même pas leur existence ni leur utilité), qui sont du coup complètement absentes de la syntaxe. Il est alors plus facile pour un dev C ou C++ qui s'est déjà probablement pris la tête avec le passage par référence et la notion de propriété (qui est responsable de la destruction de cet objet/cette structure ?) de comprendre la notion de ownership borrowing du Rust, que pour un dev Python (ouais bon la variable est détruite, donc l'objet qu'elle référence sera peut-être bien collecté au prochain passage du GC… ou pas… je m'en fous, le GC est justement là pour que j'aie pas à y penser).

Il en va de même pour la gestion des erreurs, qu'il est plus facile de construire à partir de celle du C (ex: si la valeur de retour est -1, alors il y a une erreur, sinon, c'est un résultat), ou même de Haskell (i.e. strictement la même qu'en Rust, mais enrobée dans une couche bien sucrée de monad-fapping), que dans un langage dont le zen (Explicit is better than implicit), associé à son caractère dynamique impose la notion d'exception (try/except/finally).

Mais encore une fois, je ne reproche absolument rien au rust-book ni même à Rust : j'aime beaucoup la façon dont tous ces mécanismes s'intègrent de façon cohérente dans le langage, mais je ne suis pas d'accord pour dire qu'il est plus facile de comprendre Rust quand on vient d'un langage comme Python, Perl ou JS que quand on a pratiqué C ou C++, pour moi le coût de l'apprentissage de Rust est très supérieur quand on vient d'un langage dynamique, et ce n'est pas à négliger parce que les gens apprennent de plus en plus à programmer avec Python de nos jours.

+0 -0

Je m'étais basé sur le fait que & n'a pas du tout la même signification en Rust et que tous les mécanismes C/C++ de bas niveau étaient "cachés" (on peut toujours y avoir accès, mais ce n'est généralement pas une bonne idée), rendant l'apprentissage du Rust plus compliqué que quelqu'un qui viendrait de Python ou de Javascript. En disant cela, je n'avais pas pris en compte la portée des variables par exemple, et sur ce point, c'est tout à fait l'inverse (C/C++ people comprendront mieux).

Conclusion : je suis allé trop vite en disant cela, toutes mes excuses. :)

+0 -0

Bonjour je suis en train d'apprendre le Rust avec ton tuto et en fait j'arrive pas a comprendre tout ce qu'il y a dans la partie spécificité de Rust ( notamment les traits ) je viens du python et je comprends pas grand chose de cette partie, si c'était possible qu'il y ai un peut plus d'explication dans cette partie qui je pense et la plus importante, et aussi une explication des mots clés et de ce genre de chose :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
impl<T: Display> List<T>  {

  pub fn display_all_list(&self) {

        println!("-> {}", self.a);

        match self.next {

            Some(ref n) => n.display_all_list(),

            None => {}

        }

    }
} 

le langage m’intéresse beaucoup mais je n'ai pas beaucoup d’expérience avec la programmation 3 ansa jongler entre python et javascript( je suis en terminale ), et je me dit que ce langage pourrait m'apporter beaucoup, j'ai pas envie d’échouer sur ce langage d'autant plus que ton tutoriel est très bien réalisée ( sauf pour le manque d'explication dans cette partie ) Merci !

Bonjour,

Je viens de lire assez vite ton tuto1, et je trouve qu'il manque une ligne directrice. Tu commences par dire « C'est un croisement entre langage impératif (C), objet (C++), fonctionnel (Ocaml) et concurrent (Erlang). », mais les parties fonctionnelles et concurrentes sont passées sous silence. Si c'est probablement l'exemple le plus révélateur, un manque globalement un ligne directrice. Ça fait très agglomérat syntaxique tel quelle.

Il manque une vue d'ensemble de ce qu'on peut trouver dans la bibliothèque standard. Certains éléments sont décrits ça et là, mais sans réelle logique.

Régulièrement, tu dis qu'il n'est pas possible de faire comme en C++. On s'en doute, c'est du Rust, pas du C++. Le problème est que les exemples que tu donnes sont presque toujours des erreurs que l'on pourrait faire en venant du C++, et en parler risque d'induire en erreur ceux qui, justement, ne font pas de C++ (car oui, dire « ceci ne marche pas » à quelqu'un qui ne connait pas le « ceci » en question a de bonnes chances de lui faire retenir le « ceci », c'est un problème pédagogique très important à prendre en compte). C'est contre-productif.

Dans ta deuxième partie, tu as choisis un style assez particulier, où tous les 4 paragraphes, le lecteur te pose une question ; un peu, ça rend le cours vivant, mais trop, ça le rend peu lisible (la partie « Les structures » fait très fort, notamment). On retrouve aussi le même problème pédagogique que ci-dessus : si tu dis « vous allez vous dire ça, mais non faut pas », tu risques d'apprendre l'erreur aux lecteurs. Ce genre de tournure est vraiment à éviter.

Sur les multiples erreurs de typographie, en un mot : le guillemet anglais « " » ne devrait pas être utilisé en français, tu ne mets jamais de bloc de code inline, tes légendes, listes et textes pré-bloc sont mal typographiés. Mais c'est de la forme, donc pas vraiment prioritaire à mon sens.

Bref, à part de simples éléments syntaxique, que veux-tu faire passer comme message dans ton tuto ? C'est extrêmement important, et le message passe mal.


  1. Avant de réaliser que je l'avais déjà commenté. 

+1 -0

Je tente d'enseigner les bases, mais n'ayant finalement eu que très peu de retours de mes lecteurs, il m'est difficile de comprendre ce qui pourrait poser souci. Ton retour est sans aucun doute le plus complet que je n'ai jamais eu et je t'en remercie énormément !

Il me reste encore pas mal de boulot à effectuer sur le tuto mais au moins les bases sont là.

+0 -0

Perso ce qui me décourage un peu en arrivant sur le tuto, c'est le plan.

Il laisse présager 4 grosses parties linéaires, donc longues, à lire, et ça casse tout de suite mon élan : un tuto complet sur un langage, en 4 parties, il va me falloir une semaine pour lire la première.

Gabbro parle de fil conducteur, je parlerais de progression. Si tu veux rendre ton tuto attractif, il faut le decouper en chapitres, chaque chapitre ayant un objectif, et de préférence un objectif qui se traduise par un programme concret.

Sans ça, impossible de savoir quand lever le nez pour tester, programmer, pratiquer, essayer… les notions vont se succéder et le lecteur n'aura pas posé une seule fois les mains sur son clavier avant la fin.

+0 -0

Bonjour,

J'ai parcouru ton tuto hier, et voici quelques commentaires qui j'espère, pourront t'aider. Je précise que je débute en Rust, donc si les points relevés ne sont pas pertinents ou faux, n'hésite pas à me le faire remarquer ;).

Sur ta partie sur les variables, tu passes très rapidement sur la mutabilité. Je pense que quelqu'un n'ayant pas une grande expérience en programmation n'y verra la qu'une contrainte pénible du langage, et aura donc tendance à mettre des let mut partout, pour ne pas s'embêter avec ce problème. D'autant plus que tu présentes ça sous la forme « sans mut on a une erreur, avec mut, ça marche !».
Sur cette même partie, tu parles de l'inférence de type un peu plus tard, alors que tous tes exemples avant utilisent justement l'inférence de type. Peut-être est-il plus judicieux que les exemples avant déclarent les types des variables explicitement, pour ensuite introduire l'inférence de type ?
Tu fais une remarque sur la notation i++, en soit ce n'est pas gênant, mais je trouve que ça arrive un peu comme un cheveu sur la soupe, je ne vois pas trop le rapport avec la partie (enfin si, je vois pourquoi tu précises ça, mais je ne suis pas certain que ce soit très utile, par exmple, ça n'a rien de surprenant pour quelqu'un ayant déjà fait du Python).

Quand tu parles des slices, le commentaire de la deuxième ligne est faux (« à partir du 2e élément ») :

1
2
3
4
5
6
let tab = [0, 1, 2]; // tab est un tableau contenant 0, 1 et 2
let s = &tab; // s est maintenant une slice commençant à partir du 2e élément de tab

println!("{:?}", s); // ça affichera "[0, 1, 2]"
let s = &tab[1..]; // s est maintenant une slice pointeur le 2e élément de tab
println!("{:?}", s); // ça affichera "[1, 2]"

Sur le pattern matching :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let my_string = "hello";

let s = match my_string {
    "bonjour" => "francais",
    "ciao" => "italien",
    "hello" => "anglais",
    "hola" => "espagnol",
    _ => "je ne connais pas cette langue..."
}; // on met un ';' ici car ce match retourne un type

println!("{}", s);

Le match ne retourne pas un type, mais plutôt une expression ou une valeur, non ?

Au moment où tu parles de la boucle while, il y a un qu qui traine dans ton code d'exemple ! i += 1;qu

Une petite remarque qui me vient à l'esprit, tu utilises alternativement la syntaxe let mut i = 0i32; et let mut i : i32 = 0;, peut-être est-il mieux de n'utiliser que la seconde pour une question de consistance ?

Lorsque tu parles des expressions, tu dis à un moment « une assignation de valeur retourne le type () », ne faudrait-il pas plutôt dire « la valeur () » ?

Sur la distinction tuple / struct tuple, ton exemple de tuple (struct Tuple(isize, usize, bool);) est justement un struct tuple, non ? Dans cette même partie, tu donnes un exemple contenant la ligne let Distance(longueur) = distance;, je pense que tu pourrais caser une petite phrase pour préciser que cette ligne permet de déstructurer la structure, j'ai dû faire des recherches par moi-même pour comprendre cette notation.

« À présent, venons-en au '&' devant le self : c'est la durée de vie de l'objet. » : est-ce exact de parler de durée de vie ? &self ne fait pas plutôt reférence à la notion de proprieté (référence sur self) ?

Au début du chapitre sur le déférencement, tu écris :

« Cependant, il est aussi possible de faire :

1
2
let x = String::new();
let deref_x = *x; // ce qui renvoie une erreur car le type str n'implémente pas le trait Sized

» Du coup, ce n'est pas possible de le faire, vu que ça renvoit une erreur ;). Et tu fais référence au trait Sized, ce qui est déroutant pour le lecteur qui va essayer un code qui ne compile pas, mais il ne sait pas pourquoi. Peut-être pourrais-tu écrire une petite phrase pour dire que tu vas parler de Sized plus tard ?

1
2
3
4
#![feature(box_syntax)]

let a : [u8; 100000] = [0; 100000]; // bof...
let b : Box<[u8; 100000]> = box new([0; 100000]; // mieux !

Il manque une parenthèse !

Voila les quelques remarques que j'avais pu noter au cours de ma lecture, j'avoue être passé plus rapidement sur la suite du tutoriel.
En plus de ça, quelques commentaires sur l'ensemble : je ne peux que rejoindre l'avais de Gabbro et nohar, le tutoriel ressemble un peu trop à une liste des spécifités du langage, sans trop de liens. Je pense que dans l'ensemble, tu pourrais appuyer un peu plus sur le pourquoi les créateurs de Rust ont conçu le langage d'une telle façon, et pas d'une autre, pour ne pas que le lecteur ait l'impression qu'au final, Rust ce n'est qu'une suite de contraintes dont on ne voit pas trop l'interêt.

En tout cas, merci et bravo de t'être attelé à la rédacction d'un tutoriel sur Rust, ça fait toujours plaisir de voir des ressources en français sur ce jeune langage :).

+0 -0
Ce sujet est verrouillé.