Comparaison de références

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Je continue mon petit chemin avec Rust à l’aide du book. En cherchant à me renseigner un peu plus sur l’enum option je me suis embrouillé en essayant de comparer des références vers des str. En bidouillant un peu, j’ai réussi à trouver deux fonctions qui me donne le même résultat :

fn main() {
    let names = ["Louis", "Marie", "Pierre", "Apolline"];
    
    match find_index(&names, "Pierre") {
        Some(value) => println!("Index: {}", value),
        None => println!("Name not found.")
    }

    match find_index_(&names, "Pierre") {
        Some(value) => println!("Index: {}", value),
        None => println!("Name not found.")
    }
}

fn find_index(data: &[&str], element: &str) -> Option<usize> {
    for (i, &name) in data.iter().enumerate() {
        if name == element {
            return Some(i);
        }
    }
    return None;
}

// Même résultat avec la fonction suivante :
fn find_index_(data: &[&str], element: &str) -> Option<usize> {
    for (i, name) in data.iter().enumerate() {
        if name == &element {
            return Some(i);
        }
    }
    return None;
}

Quand je reçois element dans ma fonction, c’est bien déjà une référence non ? Alors pourquoi je dois faire &element ? De même pour &name. Je me rend bien compte que la logique des deux fonctions est quasiment la même mais j’ai du mal à l’interpréter.

Merci pour votre aide !

Le plus simple, c’est de regarder le type des variables que tu manipules. Pour cela, l’astuce consiste à provoquer une erreur à la compilation en faisant une affectation vers une variable d’un type complètement délirant :

fn find_index_(data: &[&str], element: &str) -> Option<usize> {
    for (i, name) in data.iter().enumerate() {
        let truc: () = name; 
        if name == &element {
            return Some(i);
        }
    }
    return None;
}

Ici le compilateur va buter sur la ligne let truc: () = name;, et te dévoiler que dans ce contexte, name est du type &&str :

error[E0308]: mismatched types
  --> src/main.rs:27:24
   |
27 |         let truc: () = name;
   |                        ^^^^ expected (), found &&str
   |
   = note: expected type `()`
              found type `&&str`

Cela veut dire que lorsque tu itères comme tu le fais sur les éléments d’une slice, ce que tu récupères par défaut ce sont des références sur les éléments de cette slice, donc ici des références sur des &str, donc des &&str.

Dans l’autre fonction, en récupérant les éléments de l’itération avec (i, &name), tu utilises la syntaxe de pattern matching de Rust, en lui disant : "je m’attends à récupérer des tuples à deux éléments, dont je nommerai le premier i, et dont le second est une référence sur un truc que j’appellerai name" (c’est "le truc" que tu récupères dans la variable name, pas la référence), ce qui a pour effet de déréferencer le second élément (qui est un &&str), et name, dans ce contexte, sera donc du type &str, ce que tu peux vérifier de la même façon.

PS :

Quand je reçois element dans ma fonction, c’est bien déjà une référence non ?

En fait, le type &str est un petit peu particulier en Rust : c’est une "string slice", ou "une chaîne de caractères utf-8, immutable, sur laquelle on n’a pas l’ownership" (contrairement au type String qui est "une chaîne de caractères utf-8 et mutable, sur laquelle on a l’ownership"). Il faut le voir comme un type de données à part entière : oui, en toute rigueur c’est une référence, mais on ne manipule jamais des str directement.

+0 -0

Merci pour la petite technique c’est malin. ^^ Je n’hésiterai pas à en abuser.

En effet, les deux comparaisons n’avaient rien à voir. Encore un peu brouillon dans ma tête ce signe & qui référence et déréférence, j’imagine que je comprendrais avec un peu de pratique.

Prenons l’exemple suivant :

fn main() {
    let url = String::from("zestedesavoir.com");

    println!("http://{}", url);
    println!("http://{}", &url);
    println!("http://{}", &&url);
    println!("http://{}", &&&url);
    println!("http://{}", &&&&url);
}

Aucune erreur, le résultat est toujours le même. Et j’ai l’impression que je peux continuer à rajouter pas mal & devant, j’aurais toujours le même résultat. Pourquoi ?

En effet, les deux comparaisons n’avaient rien à voir. Encore un peu brouillon dans ma tête ce signe & qui référence et déréférence, j’imagine que je comprendrais avec un peu de pratique.

Wizix

Cet article peut t’aider à répondre un peu à cette question : https://jvns.ca/blog/2017/11/27/rust-ref/
Après il y a peut être des choses un peu avancées par rapport à où tu en es dans "the book"

Prenons l’exemple suivant :

fn main() {
    let url = String::from("zestedesavoir.com");

    println!("http://{}", url);
    println!("http://{}", &url);
    println!("http://{}", &&url);
    println!("http://{}", &&&url);
    println!("http://{}", &&&&url);
}

Aucune erreur, le résultat est toujours le même. Et j’ai l’impression que je peux continuer à rajouter pas mal & devant, j’aurais toujours le même résultat. Pourquoi ?

Wizix

Alors ça, c’est plus à cause du trait Display que du fonctionnement des références.

Dans la doc de ce trait, tu as la liste de ses "implémenteurs". Et en particulier tu as celui-ci :

impl<'a, T> Display for &'a T 
where
    T: Display + ?Sized, 

Si tu n’as jamais vu les lifetimes encore, tu peux faire complètement abstraction des 'a et faire comme s’ils n’étaient pas là. Cette déclaration signifie que si T est un type qui implémente Display, alors automatiquement, les références sur T implémenteront Display et se comporteront exactement pareil (la méthode fmt les déréférencera automatiquement pour afficher la vraie valeur utile).

En gros :

  • String implémente Display ;
  • donc &String implémente également Display en affichant le String qu’il référence ;
  • donc &&String implémente Display en affichant le &String qu’il référence, c’est-à-dire en affichant le String qu’il référence ;
  • etc.
+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