Uma struct, ou estrutura, é um tipo de dados personalizado que permite nomear e empacotar vários valores relacionados que formam um grupo significativo. Se você está familiarizado com uma linguagem orientada a objetos, uma struct é como os atributos de dados de um objeto. Neste capítulo, vamos comparar e contrastar tuplas com structs, demonstrar como usar structs e discutir como definir métodos e funções associadas para especificar o comportamento associado aos dados. Structs e enums (discutidos no Capítulo 6) são os blocos de construção para a criação de novos tipos no domínio do seu programa para aproveitar ao máximo a verificação de tipo de tempo de compilação do Rust.
Definindo e instanciando structs
As structs são semelhantes às tuplas, que foram discutidas no Capítulo 3. Como as tuplas, as partes de uma structs podem ser de tipos diferentes. Ao contrário das tuplas, você nomeará cada parte dos dados para que fique claro o que os valores significam. Como resultado desses nomes, as structs são mais flexíveis do que as tuplas: você não precisa depender da ordem dos dados para especificar ou acessar os valores de uma instância.
Para definir uma structs, inserimos a palavra-chave struct
e nomeamos toda a estrutura. O nome de uma struct deve descrever a importância das partes dos dados que estão sendo agrupados. Em seguida, dentro das chaves, definimos os nomes e tipos dos dados, que chamamos de campos. Por exemplo, a Listagem 5-1 mostra uma struct que armazena informações sobre uma conta de usuário.
Para usar uma structs depois de defini-la, criamos uma instância dessa estrutura especificando valores concretos para cada um dos campos. Criamos uma instância informando o nome da estrutura e, em seguida, adicionamos chaves contendo os pares chave: valor
, onde as chaves são os nomes dos campos e os valores são os dados que queremos armazenar nesses campos. Não precisamos especificar os campos na mesma ordem em que os declaramos na estrutura. Em outras palavras, a definição da structs é como um modelo geral para o tipo, e as instâncias preenchem esse modelo com dados específicos para criar valores do tipo. Por exemplo, podemos declarar um determinado Usuario conforme mostrado na Listagem 5-2.
Para obter um valor específico de uma estrutura, podemos usar a notação de ponto. Se quiséssemos apenas o endereço de e-mail desse usuário, poderíamos usar user1.email
onde quisermos usar esse valor. Se a instância for mutável, podemos alterar um valor usando a notação de ponto e atribuindo a um campo específico. A Listagem 5-3 mostra como alterar o valor no email
campo de uma Usuario
instância mutável.
Observe que toda a instância deve ser mutável; Rust não nos permite marcar apenas alguns campos como mutáveis. Como acontece com qualquer expressão, podemos construir uma nova instância da estrutura como a última expressão no corpo da função para retornar implicitamente essa nova instância.
A Listagem 5-4 mostra uma função build_user
que retorna uma instância Usuario
com o e-mail e nome de usuário fornecidos. O campo ativo
obtém o valor true
e o contagem_login
obtém o valor 1
.
Faz sentido para citar os parâmetros da função com o mesmo nome que os campos da struct, mas ter de repetir os nomes de campo e variáveis email
e nome_usuario
é um pouco tedioso. Se a struct tivesse mais campos, repetir cada um deles seria ainda mais irritante. Felizmente, existe uma abreviatura conveniente!
Usando o atalho de inicialização de campo quando variáveis e campos têm o mesmo nome
Como os nomes dos parâmetros e os nomes dos campos da struct são exatamente os mesmos na Listagem 5-4, podemos usar a sintaxe abreviada do campo init para reescrever build_user
de modo que se comporte exatamente da mesma forma, mas não tenha a repetição de email
e nome_usuario
, conforme mostrado na Listagem 5-5.
Aqui, estamos criando uma nova instância da struct Usuario
, que tem um campo chamado email
. Queremos definir o valor do campo email
para o valor no parâmetro email
da função build_user
. Como o campo email
e o parâmetro email
têm o mesmo nome, precisamos apenas escrever, email
em vez de email: email
.
Criação de instâncias de outras instâncias com sintaxe de atualização de struct
Muitas vezes é útil criar uma nova instância de uma struct que usa a maioria dos valores de uma instância antiga, mas altera alguns. Você fará isso usando a sintaxe de atualização de struct.
Primeiro, a Listagem 5-6 mostra como criamos uma nova instância de Usuario
, user2
, sem a sintaxe de atualização. Definimos novos valores para email
e nome_usuario
mas de outra forma usamos os mesmos valores de user1
que criamos na Listagem 5-2.
Usando a sintaxe de atualização de structs, podemos obter o mesmo efeito com menos código, conforme mostrado na Listagem 5-7. A sintaxe ..
especifica que os campos restantes não definidos explicitamente devem ter o mesmo valor que os campos na instância fornecida.
O código na Listagem 5-7 também cria uma instância em user2
que possui um valor diferente para email
e nome_usuario
mas possui os mesmos valores para os campos ativo
e contagem_login
de user1
.
Usando estruturas de tupla sem campos nomeados para criar tipos diferentes
Você também pode definir structs que se parecem com tuplas, chamadas de estruturas de tupla. As structs de tupla têm o significado adicionado que o nome da estrutura fornece, mas não têm nomes associados a seus campos; em vez disso, eles têm apenas os tipos dos campos. As estruturas de tupla são úteis quando você deseja dar um nome a toda a tupla e torná-la um tipo diferente de outras tuplas, e nomear cada campo como uma estrutura regular seria prolixo ou redundante.
Para definir uma estrutura de tupla, comece com a palavra-chave struct
e o nome da estrutura seguido pelos tipos na tupla. Por exemplo, aqui estão as definições e usos de duas structs de tupla chamadas Color
e Point
:
Observe que os valores black
e origin
são tipos diferentes, porque são instâncias de diferentes estruturas de tupla. Cada estrutura que você define é seu próprio tipo, embora os campos dentro da struct tenham os mesmos tipos. Por exemplo, uma função que recebe um parâmetro do tipo Color
não pode aceitar o tipo Point
como argumento, embora os dois tipos sejam compostos de três valores i32
. Caso contrário, as instâncias de struct de tupla se comportam como tuplas: você pode desestruturá-las em suas partes individuais, pode usar o .
seguido pelo índice para acessar um valor individual e assim por diante.
Estruturas semelhantes a unidades sem quaisquer campos
Você também pode definir estruturas que não possuem campos! Eles são chamados de estruturas semelhantes a unidades porque se comportam de maneira semelhante a ()
, o tipo de unidade. Estruturas semelhantes a unidades podem ser úteis em situações nas quais você precisa implementar uma característica em algum tipo, mas não tem nenhum dado que deseja armazenar no próprio tipo. Discutiremos as características no Capítulo 10.
Propriedade de Struct Data
Na definição da struct
Usuario
na Listagem 5-1, usamos o tipoString
de propriedade em vez do tipo de fatia de string&str
. Esta é uma escolha deliberada porque queremos que as instâncias desta estrutura possuam todos os seus dados e que esses dados sejam válidos enquanto a estrutura inteira for válida.É possível que os structs armazenem referências a dados pertencentes a outra coisa, mas para fazer isso requer o uso de tempos e vida, um recurso Rust que discutiremos no Capítulo 10. Os tempos de vida garantem que os dados referenciados por uma struct sejam válidos por tanto tempo como a estrutura é. Digamos que você tente armazenar uma referência em uma estrutura sem especificar tempos de vida, como este, o que não funcionará:
Nome do arquivo: src / main.rs
Esse código não compila.
struct Usuario { nome_usuario: &str, email: &str, contagem_login: u64, ativo: bool, } fn main() { let user1 = Usuario { email: "someone@example.com", nome_usuario: "someusername123", ativo: true, contagem_login: 1, }; }
O compilador reclamará que precisa de especificadores vitalícios:
$ cargo run Compiling structs v0.1.0 (file:///projects/structs) error[E0106]: missing lifetime specifier --> src/main.rs:2:15 | 2 | nome_usuario: &str, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 1 | struct Usuario<'a> { 2 | nome_usuario: &'a str, | error[E0106]: missing lifetime specifier --> src/main.rs:3:12 | 3 | email: &str, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 1 | struct Usuario<'a> { 2 | nome_usuario: &str, 3 | email: &'a str, | error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0106`. error: could not compile `structs` To learn more, run the command again with --verbose.
No Capítulo 10, discutiremos como corrigir esses erros para que você possa armazenar referências em structs, mas, por enquanto, corrigiremos erros como esses usando tipos próprios como em
String
vez de referências como&str
.
Traduzido por Acervo Lima. O original pode ser acessado aqui.
0 comentários:
Postar um comentário