57 votes

Pourquoi l'assert_eq de Rust! mis en œuvre à l'aide d'un match?

Voici l' implémentation macro assert_eq! de Rust. Je n'ai copié que la première branche par souci de brièveté:

 macro_rules! assert_eq {
    ($left:expr, $right:expr) => ({
        match (&$left, &$right) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    panic!(r#"assertion failed: `(left == right)`
  left: `{:?}`,
 right: `{:?}`"#, left_val, right_val)
                }
            }
        }
    });
}
 

Quel est le but du match ici? Pourquoi la vérification de la non-égalité n'est-elle pas suffisante?

66voto

DK. Points 1719

Bon, nous allons retirer le match.

    macro_rules! assert_eq_2 {
        ($left:expr, $right:expr) => ({
            if !($left == $right) {
                panic!(r#"assertion failed: `(left == right)`
  left: `{:?}`,
 right: `{:?}`"#, $left, $right)
            }
        });
    }

Maintenant, nous allons choisir un complètement aléatoire exemple...

fn really_complex_fn() -> i32 {
    // Hit the disk, send some network requests,
    // and mine some bitcoin, then...
    return 1;
}

assert_eq_2!(really_complex_fn(), 1);

Ce serait étendre à...

{
    if !(really_complex_fn() == 1) {
        panic!(r#"assertion failed: `(left == right)`
  left: `{:?}`,
 right: `{:?}`"#, really_complex_fn(), 1)
    }
}

Comme vous pouvez le voir, nous sommes à l'appel de la fonction à deux fois. C'est pas l'idéal, d'autant plus si le résultat de la fonction pourrait changer à chaque fois qu'il appelle.

L' match est juste un petit, moyen facile d'évaluer à la fois les "arguments" de la macro exactement une fois et de les lier à des noms de variables.

15voto

John Points 9709

À l'aide de match s'assure que les expressions $left et $right sont chaque évaluées qu'une seule fois, et que toute temporaires créés lors de leur évaluation vivre au moins, tant que le résultat liaisons left et right.

Une expansion qui a utilisé $left et $right plusieurs fois: une première fois lors de l'exécution de la comparaison, et de nouveau lors de l'interpolation dans un message d'erreur devrait se comporter de façon inattendue si l'expression avait des effets secondaires. Mais pourquoi ne pas l'expansion de faire quelque chose comme let left = &$left; let right = &$right;?

Considérer:

let vals = vec![1, 2, 3, 4].into_iter();
assert_eq!(vals.collect::<Vec<_>>().as_slice(), [1, 2, 3, 4]);

Supposons que cette élargi à:

let left = &vals.collect::<Vec<_>>().as_slice();
let right = &[1,2,3,4];
if !(*left == *right) {
    panic!("...");
}

Dans la Rouille, la durée de vie temporaires produites à l'intérieur d'un énoncé est généralement limitée à la déclaration elle-même. Par conséquent, cette expansion est une erreur:

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:5:21
   |
5  |         let left = &vals.collect::<Vec<_>>().as_slice();
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^           - temporary value dropped here while still borrowed
   |                     |
   |                     temporary value does not live long enough

Le temporaire vals.collect::<Vec<_>>() a besoin de vivre au moins aussi longtemps qu' left, mais en fait, elle est supprimée à la fin de l' let déclaration.

Cela contraste avec l'expansion

match (&vals.collect::<Vec<_>>().as_slice(), &[1,2,3,4]) {
    (left, right) => {
        if !(*left == *right) {
            panic!("...");
        }
    }
}

Cela produit le même temporaire, mais sa durée de vie s'étend sur toute la durée du match expression -- assez long pour nous de comparer left et right, et d'interpoler dans le message d'erreur si la comparaison échoue.

En ce sens, match est la Rouille de l' let ... in construire.

Notez que cette situation est inchangée avec les non-lexicale des durées de vie. En dépit de son nom, de LLN, ne change pas la durée de vie de toutes les valeurs, c'est à dire quand ils sont abandonnés. Il ne fait que le champs d'application de l' emprunte plus précis. Donc, il ne nous aide pas dans cette situation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X