web-dev-qa-db-fra.com

Rouille correspondant à une option (chaîne)

Je peux faire correspondre une chaîne dans Rust directement, comme on peut le voir dans cet exemple.

let a = "hello".to_string();

match &a[..] {
    "hello" =>{
        println!("Matches hello");
    },
    _ => panic!()
}

Mais si j'ai un type d'option, il échoue plutôt.

match Some(a){
    Some("hello") => {
        println!("Matches some hello");
    }
    _=> panic!()
}

parce que les types ne correspondent pas.

19 |         Some("hello") => {
   |              ^^^^^^^ expected struct `std::string::String`, found reference
   |
   = note: expected type `std::string::String`
              found type `&'static str`

Je ne peux pas faire l'astuce [..] car nous avons une Option. Le meilleur que Que j’ai trouvé jusqu’à présent est:

match Some(a) {
    Some(b) => {
        match(&b[..]) {
            "hello" => {
                println!("Matches some, some hello");
            },
            _=> panic!()
        }
    },
    None =>{
        panic!()
    }
}

ce qui fonctionne mais est terrible pour sa verbosité.

Dans ce cas, mon code est juste un exemple. Je ne contrôle pas la création de String ni de Some (String). Je ne peux donc pas modifier ce type dans la réalité, comme je pourrais le faire dans mon exemple.

Toutes les autres options?

7
Phil Lord

C'est une limitation connue des modèles de Rust. 

Les appels de méthode (y compris les méthodes internes pour les opérateurs tels que ==) appellent automatiquement .as_ref() si nécessaire. Ainsi, String est automatiquement transformé en &str pour permettre des comparaisons avec des littéraux.

OTOH, les modèles sont assez littéraux dans leurs comparaisons et constatent que String et &str sont différents.

Il y a deux solutions:

  1. Remplacez Option<String> par Option<&str> avant d'apparaître dessus: Some(a).as_ref().map(|s| s.as_str()). La as_ref() crée Option<&String> (interdire le déplacement), puis as_str() la référence sans ambiguïté en tant que &str.

  2. Utilisez le garde d'allumette: match Some(a) { Some(ref s) if s == "hello" => … }. Some(ref s) correspond à n’importe quelle String et le capture sous la forme s: &String, que vous pouvez ensuite comparer dans la garde if qui applique la contrainte souple habituelle pour que cela fonctionne. 

6
Kornel

Regardez ceci: https://play.Rust-lang.org/?gist=7a1d609ec43797e3fe7591feb5f8c0f9&version=stable

Vous ne pouvez pas faire correspondre sur std::String, comme vous l'avez trouvé, uniquement sur &str. Les motifs imbriqués correspondent, donc si vous pouvez faire correspondre le &str, vous pouvez le faire pour le Option<&str>, mais pas pour le Option<String>.

Dans le premier exemple, vous avez transformé le std::String en &str en effectuant &a[..]. Donc, si vous voulez apparier sur un Option<String>, vous devez faire la même chose.

Une solution consiste à utiliser des correspondances imbriquées:

match a {
  Some(ref s) => match &s[..] {
    "hello" => ...,
    _ => ...
  },
  _ => ...
}

Mais ensuite, vous devez dupliquer le code "sinon" s'il est identique, et ce n'est généralement pas aussi agréable.

Au lieu de cela, vous pouvez transformer le Option<String> en un Option<&str> et faire correspondre ce résultat à l'aide de la fonction map. Cependant, map utilise la valeur sur laquelle il est appelé, ce qui la déplace dans la fonction de mappage. C'est un problème, car vous souhaitez référencer la chaîne et vous ne pouvez pas le faire si vous l'avez déplacée dans la fonction de mappage. Donc, vous devez d’abord transformer le Option<String> en un Option<&String> et le mapper dessus.

Ainsi, vous vous retrouvez avec a.as_ref().map(|s| /* s is type &String */ &s[..]). Vous pouvez alors correspondre à cela.

  match os.as_ref().map(|s| &s[..]) {
    Some("hello") => println!("It's 'hello'"),
    // Leave out this branch if you want to treat other strings and None the same.
    Some(_) => println!("It's some other string"),
    _ => println!("It's nothing"),
  }
2
Sebastian Redl

Dans certains cas, vous pouvez utiliser unwrap_or pour remplacer None à partir de Option par une str prédéfinie que vous ne souhaitez pas gérer de manière particulière.

Je l'ai utilisé pour gérer les entrées d'utilisation:

let args: Vec<String> = env::args().collect();

match args.get(1).unwrap_or(&format!("_")).as_str() {
    "new" => { print!("new"); },
    _ => { print!("unknown string"); },
};

Ou pour correspondre à votre code:

let option = Some("hello");

match option.unwrap_or(&format!("unhandled string").as_str()) {
    "hello" => { println!("hello"); },
    _ => { println!("unknown string"); }
};
0
V Bota