web-dev-qa-db-fra.com

Pourquoi existe-t-il des variables préfixées de soulignement?

J'apprends Rust et je suis tombé sur le fait que l'ajout d'un trait de soulignement au début d'un nom de variable ne fera pas avertir le compilateur s'il n'est pas utilisé. Je me demande pourquoi cette fonctionnalité existe, car les variables inutilisées sont désapprouvées.

13
Sir Platypus

Je peux voir plusieurs raisons:

  • Vous appelez une fonction qui renvoie un #[must_use] type, mais dans votre cas particulier, vous savez que vous pouvez ignorer la valeur en toute sécurité. Il est possible d'utiliser un _ modèle pour cela (qui n'est pas une liaison de variable, c'est un modèle qui lui est propre, mais c'est probablement d'où vient la convention de préfixe de soulignement), mais vous voudrez peut-être documenter pourquoi vous ignorez la valeur, ou quelle est cette valeur . C'est particulièrement courant dans les tests d'après mon expérience.
  • Macros. Une variable créée dans une macro peut ou non être utilisée ultérieurement. Il serait gênant de ne pas pouvoir désactiver les avertissements lors d'un appel macro. Dans ce cas, il existe une convention de doublement des soulignements, ce qui est imposé par exemple par le clippy used_underscore_binding charpie.
  • RAII. Vous pouvez souhaiter qu'une variable existe pour son effet secondaire destructeur, mais ne pas l'utiliser autrement. Il n'est pas possible d'utiliser simplement _ pour ce cas d'utilisation, comme _ n'est pas une liaison de variable et la valeur serait supprimée à la fin de l'instruction.
11
mcarton

Voici quelques exemples des raisons pour lesquelles vous souhaiterez peut-être ignorer une variable inutilisée. Considérez _s Dans la fonction suivante.

fn add_numbers(f: i32, _s: i32) -> i32 {
    f + 1
}

La variable _s Permet de conserver la même signature même si nous ne l'avons pas implémentée. Cela fonctionne également si nous avons découvert que nous n'avions pas besoin du _s Mais parce que notre bibliothèque est utilisée dans de nombreux projets différents, nous ne voulions pas changer l'API pour notre fonction. Cela peut ou non être une mauvaise pratique, mais pourrait être utile dans une situation où _s Doit rester et ne rien faire. Nous pourrions également utiliser _ Ici, mais _s A potentiellement plus de sens quant à la finalité de la variable.

L'endroit suivant où cela peut être utile est lorsqu'un type implémente Drop et que vous vous souciez de l'emplacement de cette logique. Dans cet exemple, vous pouvez voir que la variable _result Est nécessaire pour que Drop se produise à la fin.

fn main() {
    let mut num = 1;
    // let _ = try_add_numbers(&mut num); // Drop is called here for _
    let _result = try_add_numbers(&mut num); // without the _result we have a warning.

    println!("{}", num);
    // Drop is called here for the _result
}

// keep the api the same even if an aurgument isn't needed anymore or
// has not been used yet.
fn add_numbers(f: i32, _s: i32) -> i32 {
    f + 1
}

// This function returns a result
fn try_add_numbers(i: &mut i32) -> Result<GoodResult, GoodResult> {
    if *i > 3 {
        return Err(GoodResult(false));
    }
    *i = add_numbers(*i, 0);
    Ok(GoodResult(true))
}

struct GoodResult(bool);

impl Drop for GoodResult {
    fn drop(&mut self) {
        let &mut GoodResult(result) = self;
        if result {
            println!("It worked");
        } else {
            println!("It failed");
        }
    }
}

Si nous utilisons let _result = try_add_numbers(&mut num); nous avons une variable qui est dans la portée jusqu'à ce que la fin de main et drop soit appelée. Si nous avions utilisé let _ = try_add_numbers(&mut num); nous n'avons toujours pas d'avertissement mais drop est appelé à la fin de l'instruction. Si nous utilisons try_add_numbers(&mut num); sans liaison let, nous obtenons un avertissement. La sortie de ce programme change en fonction de celle que nous utilisons avec notre fonction try_add_numbers.

It worked
2

ou

2
It worked

Il y a donc une utilisation pour les variables _ Et _named Qui doivent être choisies en fonction de la sortie de vos programmes. Jouez avec mon exemple sur le terrain de je pour vous en faire une idée.

1
Daniel Wilkins

Je suis tombé ici via Google en recherchant cet avertissement lié aux variables de correspondance. Ceci est tangentiellement lié.

Parfois, vous pouvez avoir du code où vous obtenez un Result et souhaitez faire correspondre les cas, mais vous ne vous souciez pas de la valeur d'erreur. À la place d'utiliser _e ou quelque chose, vous pouvez simplement utiliser _ qui ne lie pas explicitement. Voici un exemple concret. Nous ne nous soucions pas de la valeur de l'erreur car nous retournons la nôtre.

fn some_method() -> Result<u32, MyCustomError> {
    // ...
    let id: u32 = match some_str.parse() {
        Ok(value) => value,
        Err(_) => return Err(MyCustomError::Blah)
    }
    // ...
}
0
Captain Man