WEB3DEV

Cover image for Entendendo Structs e Enums em Solidity
Fatima Lima
Fatima Lima

Posted on

Entendendo Structs e Enums em Solidity

As structs permitem a criação de tipos de dados personalizados em Solidity. Uma variável declarada como sendo uma struct pode conter múltiplos tipos de dados.

Enums (Enumeráveis) são tipos de dados definidos pelo usuário que restringem uma variável a ter apenas um valor pré-definido de um conjunto de vários valores pré-definidos. Aos Enums são atribuídos valores inteiros que começam de zero até o valor do último índice.

Neste artigo, explicarei como funcionam as structs e os enums e como utilizá-los.

Começando

Será melhor se você codificar junto comigo a fim de melhor entender o material. Visite remix.ethereum.org — Este IDE online é um ambiente excelente e fácil de usar para iniciantes. Vá até a aba File Explorer, clique em contracts (contratos) e crie um novo arquivo com seu nome preferido e ".sol" no final. Vou usar "StructsAndEnums.sol" como meu nome de arquivo:

Image description

Agora, no arquivo, comece por especificar o identificador de licença. Os identificadores de licença indicam informações relevantes da licença em qualquer nível, desde o pacote até o nível do arquivo de código fonte. Certifique-se de fazer um comentário:


// SPDX-License-Identifier: Unlicense

Enter fullscreen mode Exit fullscreen mode

Especifique a versão do compilador Solidity que você deseja usar:


pragma solidity 0.8.0;

Enter fullscreen mode Exit fullscreen mode

Finalmente, crie um contrato usando a palavra-chave "contrato" e o nome que você deseja atribuir ao contrato inteligente:


contract StructsAndEnums{

}

Enter fullscreen mode Exit fullscreen mode

Este contrato vazio StructsAndEnums já é um contrato válido e pode ser utilizado em qualquer blockchain. Vá para a aba Solidity Compiler, mude a versão do compilador para a versão com a qual você está trabalhando e clique em compile (compilar). Quando a compilação for bem sucedida, você deverá ver uma marca de verificação verde ao lado do ícone do compilador:

Image description

Fantástico! Agora podemos escrever todo o nosso código de contrato inteligente em StructsAndEnums.

Tipos de variáveis na Solidity

A Solidity possui seis tipos de variáveis principais. Elas são:

  1. Strings - representadas como string.
  2. Inteiros com sinal - representados como int.
  3. Inteiros sem sinal - representados como uint.
  4. Booleanos - representados como bool.
  5. Endereços - representados como address.
  6. Bytes - representados como bytes.

Como declarar uma Struct

Para declarar uma struct, comece digitando a palavra-chave struct junto com o nome que você deseja atribuir à struct. Dentro da struct, liste os diferentes tipos de dados que você deseja que ela tenha. No trecho do código abaixo, eu tenho uma struct chamada "Shipment" com alguns nomes e tipos de variáveis nela:


struct Shipment {

    string package;

    string state;

    address sender;

    address receiver;

    uint price;

 bool delivered;

}

Enter fullscreen mode Exit fullscreen mode

Criando variáveis Struct

Para criar uma variável struct, digite o nome da struct seguida do nome de sua variável. Você precisará analisar os valores dos diferentes tipos de dados da struct. Uma maneira de fazer isto é atribuindo os valores de acordo com a ordem do tipo de dados na struct, assim:


Shipment purchase = Shipment("Flash Drive", "Texas", 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB, 20, true);

Enter fullscreen mode Exit fullscreen mode

Outra forma é atribuindo os valores em qualquer ordem com a chave da variável e o valor:


Shipment purchase = Shipment({price: 20, sender:0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, package: "Flash Drive", delivered: true, receiver:0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB});

Para ver a variável, temos que atribuí-la à senha pública:

Shipment public purchase = Shipment("Flash Drive", "Texas", 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB, 20, true);

Enter fullscreen mode Exit fullscreen mode

Vamos tentar analisar as variáveis armazenadas nesta variável struct chamada purchase. Primeiro, temos que implantar o contrato. Vamos implantar para a máquina virtual remix, uma simulação utilizada para testes por desenvolvedores e não uma rede de blocos real. Com quaisquer novas mudanças que acrescentarmos ao contrato inteligente, estaremos implantando novos contratos e eliminando os anteriores para ver as mudanças. Por natureza, os contratos inteligentes são imutáveis e nunca poderão ser editados após a implantação. Vá para a aba de transações Deploy & run transactions na parte inferior e clique em deploy.

Image description

Após uma implantação bem sucedida, na parte inferior, você encontrará uma seção de Deployed Contracts (Contratos Implantados) que contém o contrato que você acabou de implantar. Quando você clicar no menu suspenso, você encontrará a variável pública chamada purchase que criamos anteriormente em nosso código. Clique no botão purchase. O Remix retornará as variáveis armazenadas na variável struct da purchase. Aqui está como ficará a saída:

Image description

Atualizando as variáveis Struct

Então, criamos uma struct e uma variável struct, mas e se quiséssemos mudar certas coisas nessa variável? E se quiséssemos mudar os valores dentro da variável purchase de fora do contrato? Vamos criar uma função que faça exatamente isso.

Vamos nomear a função update e torná-la pública. Esta função tomará todas as variáveis necessárias para a variável struct. Os tipos de variáveis como strings e bytes são armazenados em locais de dados. A palavra-chave memory diz para a Solidity armazenar temporariamente os dados de entrada da variável string enquanto uma função está sendo chamada.


function update (string memory _package, string memory _state, 

    address _sender, address _receiver, uint _price, bool _delivered) public{

        purchase = Shipment(_package, _state, _sender, _receiver, _price, _delivered);

}

Enter fullscreen mode Exit fullscreen mode

Uma vez compilado e implantado este código, devemos ser capazes de ver dois botões na seção Deployed Contracts. Um deles é a variável struct purchase e a função update. Se clicarmos sobre purchase, veremos as variáveis de antes:

Image description

Vamos tentar mudar estes valores. Então, à direita do botão update, clique no ícone suspenso, preencha seus valores preferidos e clique em transact:

Image description

E, uma vez que clicamos em purchase novamente:

Image description

Que bom! Fomos capazes de mudar os valores em purchase com sucesso.

Como declarar um Enum

Para declarar um enum, digite a palavra-chave enum antes do nome que você deseja atribuir ao enum. Os enums são um pouco parecidos com os booleanos. A única diferença entre eles é que os booleanos só podem ter duas opções que são "0" (falso) e "1" (verdadeiro), enquanto os enums podem ter mais de duas opções. Vejamos um exemplo de um enum com quatro opções:


enum Status {

    None,

    Pending,

    Shipped,

    Received

}

Enter fullscreen mode Exit fullscreen mode

Criando variáveis do tipo Enum

Para atribuir uma variável a um enum, à semelhança do que fizemos anteriormente com as structs, temos o nome do enum na frente da variável e a declaramos pública para que possamos ver seu valor:


Status public orderStatus;

Enter fullscreen mode Exit fullscreen mode

E quando compilamos e implantamos isto:

Image description

Recebemos um valor 0, que é a primeira escolha atribuída ao enum (None). Os enums sempre usam o primeiro caso como padrão. Poderíamos também ter feito uma escolha diferente para orderStatus e seria atribuído esse valor.

Atualizando variáveis Enum

Vamos usar uma função semelhante à que criamos para as structs. A função será chamada de change e terá um único parâmetro do tipo uint:


function change(uint value) public {

  orderStatus = Status(value);

}

Enter fullscreen mode Exit fullscreen mode

E uma vez isto compilado e implantado:

Image description

O valor do OrderStatus está agora no terceiro índice (2) que é "Shipped".

Usando um Enum numa Struct

Uma vez que uma struct é uma compilação de diferentes tipos de dados, ela também pode receber enums. Vamos substituir a variável booleana chamada delivered (entregue) por um enum:


struct Shipment {

  string package;

  string state;

  address sender;

  address receiver;

  uint price;

  Status delivered;

}

Enter fullscreen mode Exit fullscreen mode

Antes de compilar, vamos fazer algumas mudanças. De volta a nossa variável purchase, mudar:


Shipment public purchase = Shipment("Flash Drive", "Texas", 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB, 20, true);

Enter fullscreen mode Exit fullscreen mode

Para isto:


Shipment public purchase = Shipment("Flash Drive", "Texas",0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB, 20, Status(0));

Enter fullscreen mode Exit fullscreen mode

Faça o mesmo para a função update. Mude isto:


function update(string memory _package, string memory _state, 

address _sender, address _receiver, uint _price, bool _delivered) public{

    purchase = Shipment(_package, _state, _sender, _receiver, _price, _delivered);

}

Enter fullscreen mode Exit fullscreen mode

Para isto:


function update(string memory _package, string memory _state, 

address _sender, address _receiver, uint _price, Status _delivered) public{

    purchase = Shipment(_package, _state, _sender, _receiver, _price, _delivered);

}

Enter fullscreen mode Exit fullscreen mode

Agora compile e implante o contrato. Para inserir o valor para o tipo de dados enum, ao chamar a função update, digite o valor uint para a opção que você deseja usar. Por exemplo, se quisermos que a opção seja "Pending" (Pendente), nosso valor de entrada tem que ser 1.

Armazenando Structs e Enums num mapping

Os Mappings (mapeamentos) atuam como tabelas de hash ou dicionários em outros idiomas. Podemos usar um mapeamento para armazenar múltiplos dados na blockchain com uma chave. Os mapeamentos são mais precisos e também mais eficientes do que as arrays. Manteremos o status de enum na struct Shipment como fizemos anteriormente, mas armazenaremos a struct em um mapeamento chamado getPurchase. A chave para o mapeamento será o endereço do remetente e o valor será os dados da struct atual do Shipment vinculada a esse endereço. Para declarar um mapeamento, digite a palavra-chave do mapeamento junto com a chave e o valor do mapeamento entre parênteses e um nome para o mapeamento no final.:


mapping(address => Shipment) public getPurchase;

Enter fullscreen mode Exit fullscreen mode

Na função update, vamos receber novas variáveis estruturais assim como novas alterações feitas nas variáveis structs existentes e armazená-las no mapeamento. Vamos fazer do remetente o verdadeiro remetente da transação alterando _sender para a palavra-chave msg.sender. Depois disso, não precisaremos mais do address_sender como um parâmetro na função:


function update(string memory _package, string memory _state, address _receiver, uint _price, Status _delivered) public{

  purchase = Shipment(_package, _state, msg.sender, _receiver, _price, _delivered);

  getPurchase[msg.sender] = purchase;

}

Enter fullscreen mode Exit fullscreen mode

Depois de compilarmos e implementarmos, vamos inserir novos valores para a função update:

Image description

Vamos procurar os dados do remetente no mapeamento getPurchase entrando com o endereço do msg.sender como a chave. Para obter seu endereço, vá até o topo da aba "Deploy & Run Transactions" e você verá uma lista de endereços virtuais criados pelo remix::

Image description

Copiar o primeiro endereço usado para fazer a transação e inserir esse endereço no mapeamento getPurchase:

Image description

Ótimo! Todos os endereços de remetentes novos e existentes que interagem com a função update também terão seus dados structs e enums armazenados ou atualizados no mapeamento. Para testar isso ainda mais, você pode tentar mudar para múltiplos endereços de conta no topo, interagindo com a função update de cada um e inserindo esses endereços como a chave para o mapeamento..

Conclusão

As structs e os enums reduzem a complexidade e facilitam a leitura e compreensão de seu código por parte de outros desenvolvedores. Sempre que você receber um erro de seu compilador que diga: "Stack muito profundo. Tente remover variáveis locais", isso significa que você criou muitas variáveis locais dentro de uma única função. Usar uma struct para armazenar essas variáveis locais permite que você supere esse erro. O uso de structs e enums também é excelente para otimização.

Esse artigo foi escrito em Pieces e traduzido por Fátima Lima. Seu original pode ser lido aqui.

Oldest comments (0)