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:
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
Especifique a versão do compilador Solidity que você deseja usar:
pragma solidity 0.8.0;
Finalmente, crie um contrato usando a palavra-chave "contrato" e o nome que você deseja atribuir ao contrato inteligente:
contract StructsAndEnums{
}
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:
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:
- Strings - representadas como string.
- Inteiros com sinal - representados como int.
- Inteiros sem sinal - representados como uint.
- Booleanos - representados como bool.
- Endereços - representados como address.
- 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;
}
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);
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);
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.
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:
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);
}
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:
Vamos tentar mudar estes valores. Então, à direita do botão update
, clique no ícone suspenso, preencha seus valores preferidos e clique em transact:
E, uma vez que clicamos em purchase novamente:
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
}
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;
E quando compilamos e implantamos isto:
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);
}
E uma vez isto compilado e implantado:
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;
}
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);
Para isto:
Shipment public purchase = Shipment("Flash Drive", "Texas",0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB, 20, Status(0));
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);
}
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);
}
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;
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;
}
Depois de compilarmos e implementarmos, vamos inserir novos valores para a função update
:
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::
Copiar o primeiro endereço usado para fazer a transação e inserir esse endereço no mapeamento getPurchase
:
Ó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.
Latest comments (0)