Modelar registros com structs e métodos

RustBeginner
Pratique Agora

Introdução

Variáveis separadas funcionam em exemplos pequenos, mas programas reais geralmente precisam manter dados relacionados juntos em um único valor. Uma struct do Rust define o formato desse registro, e um bloco impl fornece as operações que pertencem a ele.

Você completará um modelo InventoryItem preparado em quatro pequenas alterações: definir os campos, construir um item válido, calcular o valor do estoque por meio de &self e atualizar a quantidade por meio de &mut self. O programa final comprovará tanto o estado armazenado quanto o comportamento dos métodos.

Definir o registro de inventário

Nesta etapa, você definirá os três dados que pertencem a um item de inventário.

Entre no projeto Cargo preparado e abra o arquivo-fonte:

cd /home/labex/project/inventory-item
nano src/main.rs

A declaração struct InventoryItem { ... } introduz um novo tipo de registro. Dentro das chaves, cada campo tem um nome seguido de dois-pontos e do tipo correspondente. Todo valor de InventoryItem deve fornecer todos esses campos.

Substitua o comentário da Etapa 1 por estas três linhas de campos:

    name: String,
    price_cents: u32,
    quantity: u32,

name armazena texto, enquanto os dois campos u32 armazenam números inteiros não negativos. As vírgulas finais separam os campos e facilitam a adição ou a reorganização das linhas posteriormente.

Salve com Ctrl+O, pressione Enter e saia com Ctrl+X. Peça ao Cargo para verificar as definições de tipos:

cargo check

Um resultado Finished sem avisos significa que o formato da struct é válido e que cada valor preparado tem um uso definido. O construtor ainda contém todo!, um marcador temporário para código incompleto. O Rust permite esse marcador durante cargo check, mas executar um caminho que chegue a todo! interromperia o programa com um panic. Você o substituirá antes de executar o programa na próxima etapa.

Construir um item completo

Nesta etapa, você transformará a função associada new em um construtor que retorna um InventoryItem completo.

O bloco impl InventoryItem agrupa operações relacionadas ao tipo. new é uma função associada porque não recebe self; ela é chamada por meio do tipo, usando InventoryItem::new(...). Um método como value_cents recebe self e é chamado por meio de um valor, usando item.value_cents(). O tipo de retorno Self é outro nome para InventoryItem dentro deste bloco impl.

Abra o código-fonte:

nano src/main.rs

Dentro de new, substitua a linha inteira todo!(...) por:

        Self { name, price_cents, quantity }

Esta é uma expressão de struct. A abreviação de campos do Rust permite que name signifique name: name quando o parâmetro e o campo têm o mesmo nome. A expressão não tem ponto e vírgula porque é o valor retornado pela função.

Salve e saia. Em seguida, verifique e execute o programa:

cargo check
cargo run --quiet

A saída atual deve ser:

Preparing Notebook at 250 cents with 4 units
Item: Notebook
Unit price: 250 cents
Starting value: 0 cents
Adding 3 units
Quantity after restock: 4
Final value: 0 cents

Agora o item existe com os valores de campo fornecidos. Os zeros e a quantidade inalterada vêm dos dois marcadores restantes nos métodos, portanto são esperados nesta etapa.

Calcular o valor com &self

Nesta etapa, você completará um método que lê dois campos sem assumir a propriedade do item nem alterá-lo.

O parâmetro &self é uma referência compartilhada ao InventoryItem atual. Ele é uma forma abreviada de um valor emprestado cujo tipo concreto é &InventoryItem. O método recebe essa referência automaticamente quando você chama item.value_cents().

Abra o código-fonte:

nano src/main.rs

Dentro de value_cents, substitua somente o marcador 0 por:

        self.price_cents * self.quantity

O ponto seleciona campos do item emprestado. A multiplicação produz o valor total em centavos, e a expressão se torna o valor u32 retornado pelo método porque não tem ponto e vírgula no final.

Salve e saia. Em seguida, execute:

cargo run --quiet

As linhas de valor agora devem ser:

Starting value: 1000 cents
...
Final value: 1000 cents

Quatro unidades a 250 centavos equivalem a 1000 centavos. As duas linhas ainda coincidem porque restock ainda não alterou a quantidade; essa alteração controlada é a etapa final.

Repor o estoque com &mut self

Nesta etapa, você completará um método que altera um campo por meio de um empréstimo exclusivo.

O receptor &mut self é uma referência mutável ao item atual. Ele permite que o método atualize campos sem assumir a propriedade do item. A variável em main já foi declarada com let mut item, portanto pode fornecer esse acesso mutável temporário.

Abra o código-fonte:

nano src/main.rs

Substitua o comentário da Etapa 4 dentro de restock por:

        self.quantity += amount;

O operador += adiciona o valor à direita ao campo existente e armazena o resultado de volta nesse campo. Esta é uma instrução, por isso termina com ponto e vírgula.

Salve e saia. Em seguida, verifique e execute o modelo concluído:

cargo check
cargo run --quiet

A saída completa deve ser:

Preparing Notebook at 250 cents with 4 units
Item: Notebook
Unit price: 250 cents
Starting value: 1000 cents
Adding 3 units
Quantity after restock: 7
Final value: 1750 cents

A quantidade muda de quatro para sete, e o mesmo método value_cents observa o novo estado. O empréstimo mutável termina após restock, portanto o código posterior pode ler o item normalmente.

Resumo

Você definiu uma struct que mantém campos relacionados juntos, criou valores por meio de um construtor Self, leu o estado com um método &self e atualizou o estado com um método &mut self. A saída final do inventário comprovou que construção, cálculo e alteração funcionam em conjunto.