Como combinar testes para variante e valor de enum?

Sep 08 2020

Em Rust, suponha que eu tenha um enum e gostaria de fazer algo condicional a sua variante e algum teste envolvendo seu valor. Estou tendo problemas para encontrar uma boa maneira de escrever isso.

Por exemplo, digamos que eu tenha um enum para representar animais de fazenda, com a variante fornecendo suas espécies e o valor fornecendo alguns outros atributos, como seu peso.

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

Eu quero uma função para alimentar um animal. A maioria dos animais obtém feno, mas um cavalo com menos de 700 quilos obtém cenouras.

Intuitivamente, eu quero escrever

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

mas recebo erros de compilação " letexpressões nesta posição são experimentais" e "expressão esperada, declaração encontrada ( let)".

Ao invés eu posso escrever

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

mas preciso duplicar o // feed haycódigo, o que pode ser problemático se for longo ou mudar com frequência.

Eu tenho o mesmo problema se eu usar match: eu posso colocar um teste if weight < 700dentro do Horsebraço, mas então eu voltar a ter de incluir // feed hayna sua else, bem como no _braço.

Existe uma maneira idiomática padrão de escrever isso sem duplicar o código? Isso pode ser uma FAQ, mas posso estar procurando os termos errados.

Estou usando o rustc 1.46.0.

Respostas

4 FlorianWeimer Sep 08 2020 at 00:47

Os guardas de jogo já existem há algum tempo (o Debian buster 1.34.2 já os tem) e suportam este caso de uso de uma forma menos prolixa:

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

Alternativamente, você pode mover a parte do feno da alimentação para uma função local e chamá-la de vários lugares.