Modelando algoritmos complexos com enum
Tags: rust, enum, programming
Published: 2022-12-24 12:33:05

Em Rust, os **enumerados** são tipos muito ricos. [Neste vídeo](https://www.youtube.com/watch?v=-QCRIlftT4U) eu dei uma pequena introdução aos enumerados em Rust. É bem comum você encontrar na internet exemplos e tutoriais sobre enumerados para modelar problemas simples. Agora eu vou dar exemplos um pouco mais avançados, mas que não são muito difíceis. Na prática, vamos aplicar a técnica de composição/decomposição usando várias camadas de enumerados. 

Para ilustrar o poder dos enumerados, vamos modelar um jogo de xadrez, o que pode ser considerado um problema bastante complexo. Na verdade, nós vamos modelar apenas uma parte bem modesta do jogo, porém repleta de desafios, principalmente se você pensar numa abordagem clássica de algoritmos. 

## Modelando um jogo de xadrez básico

No xadrez temos seis tipos de peças, mas antes vamos começar com um objetivo bem básico: simular as cores das peças.

```rust
enum Cor {
  Brancas,
  Pretas }
```
E agora nós podemos declarar as peças
```rust
#[derive(Debug, Copy, Clone, PartialEq)]
enum TipoPeca {
  Peao,
  Cavalo,
  Bispo,
  Torre,
  Dama,
  Rei }
```

De cara eu já anotei uma série de **macros** para adicionar alguns recursos básicos para o nosso tipo, como por exemplo algumas otimizações da memória. Mas não se preocupe com isso por enquanto.

Um terceiro tipo poderia encapsular os dois primeiros. Esta é uma ótima forma de esconder a complexidade.

```rust
#[derive(Copy, Clone)]
struct Peca {
  cor: Cor,
  peca: TipoPeca 
}  
```

```diff
+  #[derive(Copy, Clone)]
=  enum Cor {
=    Brancas,
=    Pretas }
```


E eu posso criar qualquer peça de xadrez assim:

```rust
use Cor::*;
use TipoPeca ::*;
let peca1 = Peca {
  cor: Brancas,
  peca: Cavalo,
}
```

## Representando um tabuleiro 

Um tabuleiro é uma matriz de `8X8` o que resulta em **64** casas. Uma casa pode estar ocupada por qualquer peça, mas uma casa também pode estar vazia. É aqui que começa a ficar interessante:

```rust
#[derive(Copy, Clone)]
enum Casa{
  Ocupada(Peca),
  Vazia
}  
```

Veja que, neste caso, estamos abstraindo a seguinte condição lógica

```
Se a casa está ocupada, então ela possui uma Peça
Senão, a casa está vazia
```

Isso é muito poderoso e pode ser explorado de uma forma muito particular.

```rust
use Casa::*;
var casa: Casa;
...
match casa {
  Ocupada(_peca) => print!("♟"),
  Vazia => print!("🔲"),
}
```

Um ponto interessante é que eu posso adicionar **fields** para um enumerado. Desta forma eu posso identificar qual peça está ocupando aquela casa. No exemplo acima eu criei um único _field_ do tipo `Peca`, mas veja que poderiam ser adicionados vários _fields_ dentro da tupla. Também seria possível adicionar _fields_ nomeados, usando a sintaxe de estruturas.

```rust
enum Exemplo {
  ocupada{peca: Peca, cor: Cor},
  vazia(Cor,)
}
```

## Adicionando comportamento

Uma das tendências da programação Rust é separar os algoritmos em duas camadas: A camada de dados e a camada de comportamento. Esta técnicas é, por vezes, chamada de **programação orientada a dados**. Quando criamos enumerados  **enum** e estruturas **struct** em geral nós estamos modelando a camada de dados, porque estas estruturas que irão armazenar as variáveis do sistema. E então temos as **traits**, que permitem criar métodos abstratos, e as cláusulas **impl** que permitem implementar métodos para as nossas estruturas. Neste artigo nós não vamos abordar _traits_, porém veremos que também é possível adicionar rotinas de comportamento para enumerados.

```rust
impl TipoPeca {
  fn display(&self) -> char {
    match self {
      Peao => '🟤',
      Cavalo => '🏇',
      Bispo => '🧙',
      Torre => '🚩',
      Dama => '🌸',
      Rei => '👑'
  }
}
```

Se os enumerados simulam uma máquina de estados, os métodos também podem ser compreendidos como funções que alterem o estado da variável.

```rust
impl TipoPeca {
  fn promover(&self) {
    match self {
      TipoPeca::Peao => self = TipoPecao::Dama,
      outra => panic!("🛑 Esta peça {} não pode ser promovida ", outra.display);
    }
  }
}
```


A aplicação final ficaria mais ou menos assim:

```rust
use Cor::*;
use TipoPeca ::*;
use Casa::*;
let mut tabuleiro = [Vazia;64];
let torre = Peca{
  cor: Brancas,
  peca: Torre};
let bispo = Peca{
  cor: Brancas,
  peca: Bispo};
...
tabuleiro[0] = Ocupada(torre);
tabuleiro[1] = Ocupada(bispo);
for casa in tabuleiro.iter() {
match casa {
  Ocupada(p) => print!("{}", p.peca.display),
  Vazia => print!("🔲"),
  }
}
```
Aqui está o [código ↗](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1165034bc154e886a522450daa8fb1b9) para quem quiser brincar.
Bom jogo! 🦀

## Sugestões de leitura

Você pode apoiar o meu livro de Rust no [Catarse](https://www.catarse.me/rust?ref=user_contributed)

Se você gostar deste artigo, é bem provável que também goste desta incrível abordagem que [Joshua Cooper](https://blog.logrocket.com/json-and-rust-why-serde_json-is-the-top-choice/) sobre a criação de apis e modelagem de documentos JSON com enumerados.