Come combinare i test per la variante enum e il valore?

Sep 08 2020

In Rust, supponiamo che io abbia un enum e vorrei fare qualcosa di condizionato alla sua variante e qualche test che coinvolga il suo valore. Non riesco a trovare un buon modo per scrivere questo.

Ad esempio, diciamo che ho un'enumerazione per rappresentare gli animali da fattoria, con la variante che fornisce la loro specie e il valore che dà alcuni altri attributi come il loro peso.

enum Animal {
    Horse(i32),
    Cow(i32, i32),
    Sheep,
    // and so on
}

Voglio una funzione per nutrire un animale. La maggior parte degli animali riceve il fieno, ma un cavallo che pesa meno di 700 chili riceve invece le carote.

Intuitivamente voglio scrivere

fn feed(animal: Animal) {
    if let Animal::Horse(weight) = animal && weight < 700 { // ferris_is_confused.png
        // feed carrots
    } else {
        // feed hay
    }
}

ma ottengo errori di compilazione "le letespressioni in questa posizione sono sperimentali" e "espressione attesa, dichiarazione trovata ( let)".

Posso invece scrivere

fn feed(animal: Animal) {
    if let Animal::Horse(weight) = animal {
        if weight < 700 {
            // feed carrots
        } else {
            // feed hay
        }
    } else {
        // feed hay
    }
}

ma poi devo duplicare il // feed haycodice, che potrebbe essere problematico se è lungo o cambia frequentemente.

Ho lo stesso problema se lo uso match: posso mettere un test if weight < 700all'interno del Horsebraccio, ma poi devo di nuovo includere // feed haynel suo elsecosì come nel _braccio.

Esiste un modo idiomatico standard per scrivere questo senza duplicare il codice? Questa potrebbe essere una FAQ, ma potrei cercare i termini sbagliati.

Sto usando rustc 1.46.0.

Risposte

4 FlorianWeimer Sep 08 2020 at 00:47

I match guard sono in circolazione da un po 'di tempo (la versione 1.34.2 di Debian buster li ha già) e supportano questo caso d'uso in modo meno prolisso:

fn feed(animal: Animal) {
   match animal {
     Animal::Horse(weight) if weight < 700 => {
       // feed carrots
     }
     _ => {
       // feed hay
     }
   }
}

In alternativa, puoi spostare la parte di fieno del feed in una funzione locale e chiamarla da più punti.