J'apprend le Rust ( j'essaye )

Je suit le tuto sur Rust que j'ai trouvé sur un zeste de savoir mais j'ai du mal avec certaine notion ...

a marqué ce sujet comme résolu.

Salut à tous, je suis nouveau sur ce site, car ne trouvant pas d'aide sur d'autres forums que j'ai l'habitude de fréquenter et ayant vus un sujet parlant du Rust sur un zest de savoir https://zestedesavoir.com/forums/sujet/3457/apprendre-le-rust/ , je me suis dit peut-être que certains d'entre vous sont apte à m'aider.

Venant du python en passant pas le Javascript je voulais apprendre un langage compilé et je me suis lancé dans l'apprentissage du rust je suis arrivé au chapitre sur les traits https://zestedesavoir.com/forums/sujet/3457/apprendre-le-rust/ et la je comprends plus rien, je comprends pas comment créer son trait, a quoi ça sert etc…

Donc si certain d'entre vous on le temps et la patience de m'expliquer je les écouterais avec la plus grande attention ! Merci.

Je n'ai lu que très rapidement, mais si je comprends bien, les traits sont des comportements par défauts que doivent implémenter les objets qui les utilisent.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
trait Animal {
    fn get_espece(&self) -> String {
        String::from("Inconnue")
    }

    fn presentation(&self) -> String {
        format!("Je suis un {} !", self.get_espece())
    }
}

impl Animal for Chat {
    pub fn get_espece(&self) -> String {
        String::from("Chat")
    }
}

Vu que Chat implémente le trait Animal, les méthodes requises par ce trait doivent être implémentées par Chat. Là je m'avance, mais ça ressemble beaucoup aux interfaces proposées dans plusieurs langages ou aux traits du C++.

Salut.

Le C++ ne propose pas vraiment de concept équivalent (le plus proche serait une classe avec des fonctions membres possiblement pures, un destructeur protégé si il n'y a aucune fonction virtuelle pure, et surtout aucune variable membre).

Si tu veux apprendre le Rust, passe par un tuto sur Rust.

+0 -0

La doc en anglais est plutôt bien (complète et pédagogique), et je te la conseille vraiment si tu veux apprendre Rust en ce moment.

Sinon tu peut aussi poser tes questions par ici, on essayera d'y répondre.

je comprends pas comment créer son trait, a quoi ça sert etc…

Comment le créer, c'est juste de la syntaxe. À quoi ça sert c'est plus important. Comme tu n'as pas d'héritage en Rust, la manière de faire est de passer par les traits. Un trait décrit un comportement qui est partagé par plusieurs types d'objets dans le code. En python, ça se rapproche des méthodes magiques (__str__ => Display, __add__ => Add, …).

Les traits sont un ensemble de fonctions qui définissent une interface et un comportement, et qui permettent de faire en sorte que le code soit facile à étendre depuis l'extérieur. Une bibliothèque peut définir un trait Foo, et un ensemble de fonction qui opèrent sur ce trait fn foobar<T: Foo>(...). En tant qu'utilisateur de la bibliothèque, il te suffit d'implémenter ce trait pour avoir accès à l'intégralité de ces fonctions.

Les traits servent aussi à la programmation générique en Rust. Ce sont eux qui permettent aux templates Rust d'être vérifiés à la compilation, et pas à l’instanciation comme en C++. Ils sont aussi à la base du dispatch statique et dynamique, mais il n'y a pas d'équivalent à ça en Python.

+0 -0

Alors, ce n’est pas étonnant que tu aies du mal à comprendre, parce que les traits de Rust sont en fait un concept qui vient de la programmation fonctionnelle : habituellement, on appelle cela des classes de types (et non, ça n’a pas grand chose à voir avec les classes de la programmation orientée objet (oui, c’est casse-bonbons ^^ )).

Les langages fonctionnels, et le Rust, ne permettent pas la surcharge de fonctions et d’opérateurs comme on peut le faire en C++. À la place, on va définir un ensemble de fonctions décrivant un comportement : par exemple, la classe de types Eq en Haskell définit le comportement « peut être comparé » et regroupe les fonctions == et /= (en C++, on aurait !=) ; la classe de types Num définit le comportement « se comporte comme un nombre » et définit l’addition, la multiplication, l’inverse, la valeur absolue et 2-3 autres fonctions similaires.

On va ensuite définir un type quelconque, et dire que ce type implémente la classe de types voulue, ce qui va nous permettre de définir le fonctionnement de ces fonctions pour ce type-là, alors même que des fonctions du même nom existent déjà.

En Rust, c’est pareil, sauf que cette implémentation de fonctions « génériques » se fait avec une syntaxe similaire à celle des méthodes (impl et utilisation par objet.fonction()), ce dont je n’ai jamais compris l’utilité.

Il y a également autre chose. Je suppose que tu as eu l’occasion de voir les fonctions paramétrées, équivalent à peu près aux templates du C++ ? Eh bien les traits/classes de types te permettent de poser ce que l’on appelle des contraintes de types sur les paramètres de ces fonctions : tu vas spécifier que tous les types ne sont pas acceptés en paramètre, mais uniquement ceux qui implémentent un certain trait.

Par exemple, pour reprendre l’exemple de informaticienzero, tu pourras écrire une fonction dis_moi_bonjour() qui n’accepte que les paramètres qui implémentent le trait Animal, et dont on est alors certain qu’ils possèdent la fonction presentation().

Il n’y a à l’heure actuelle pas d’équivalent exact de cela en C++, mais c’est prévu pour le prochain standard.

TL;DR : ça sert à faire de la surcharge de fonctions et c’est le seul moyen d’en faire en Rust. Ça aide aussi à faire de meilleures fonctions paramétrées.

+0 -0

Moui ? Le fait de surcharger une fonction en la réécrivant avec de nouveaux paramètres n’est pas spécifique au Haskell, hein, on trouve ça aussi en C++. Genre…

1
2
3
4
5
6
7
int max(int a, int b) {
    // Code
}

float max(float a, float b) {
    // Code
}

Alors qu’en Rust, on va nécessairement se retrouver avec…

1
a.max(b)

Je ne vois pas trop en quoi c’est plus simple à appréhender…

+0 -0

Ok, j'avais mal compris ta remarque. Je pensais que tu proposais d'utiliser une syntaxe type Haskell pour les surcharges.

Toutefois, ton exemple n'est pas suffisant: avec l'UFCS, on peut écrire sans problème Max::max(a, b):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
trait Max {
    fn max(self, other: Self) -> Self;
}

impl Max for usize {
    fn max(self, _: usize) -> usize {
        return self;
    }
}

impl Max for f64 {
    fn max(self, other: f64) -> f64 {
        return other;
    }
}

fn main() {
    let a = 1.0;
    let b = 42.0;
    let c = Max::max(a, b);
    println!("{}", c);

    let a = 1;
    let b = 3;
    let c = Max::max(a, b);
    println!("{}", c);
}
+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte