Alura > Cursos de Programação > Cursos de > Conteúdos de > Primeiras aulas do curso Rust: aprenda mais sobre tipos

Rust: aprenda mais sobre tipos

Arrays - Apresentação

Olá, pessoal. Sejam muito bem-vindos à Alura! Sou o Vinicius Dias e guiarei vocês nesse treinamento de Rust, em que vamos conhecer um pouco mais sobre alguns tipos de Rust. Já falamos em um treinamento anterior sobre sintaxe básica, um pouco de Pattern Matching e até alguns tipos primitivos como inteiros, números de ponto flutuante (ou decimais), booleanos, caracteres etc. Agora, nesse treinamento, vamos evoluir e falar de tipos um pouco mais complexos.

Primeiro, vamos aprender sobre alguns conjuntos de dados, por exemplo, como alocar múltiplas variáveis de um mesmo tipo. Se quisermos armazenar notas de provas em uma mesma variável — ou seja, vários números decimais em uma única variável —, como fazemos? Para isso, aprenderemos sobre arrays.

Depois, vamos estudar como criar nossos próprios tipos. Se temos um tipo em que os valores são delimitados e já conhecidos, podemos utilizar uma estrutura chamada Enum. Criaremos uma Enum com dias da semana e outra com cores, e descobriremos como passar valores para essa Enum sobre cores.

Nesse processo, vamos perceber que é possível passar valores para Enums em mais de um formato, seja através de formatos de tupla (ou seja, somente informando os tipos) ou em formato de struct, em que informamos o nome e o valor.

Em seguida, vamos falar sobre uma Enum específica, a de Option. No treinamento anterior, estudamos o Result, e Option é bastante parecida. Nesse curso, veremos o valor desse tipo de Enum.

Entendido o Option, vamos partir para um "tipo" de array, ou seja, um vector — uma estrutura chamada Vec que é basicamente um array dinâmico, em que não precisamos saber o tamanho dele e podemos ir alocando conforme necessário. Nesse ponto, vamos até falar um pouco sobre performance e como tentar evitar realocações de memória.

Depois de aprender isso tudo, vamos ver como agrupar dados que faz sentido estarem juntos. Por exemplo, se quisermos representar uma conta-corrente com titular e saldo, como agrupamos esses valores de modo que fiquem juntos? Para isso, aprenderemos sobre o struct. Vamos, inclusive, aprender a criar métodos para essa struct.

Então, nesse treinamento, vamos estudar sobre como criar nossos tipos, alguns tipos de Rust, conjunto de dados etc. Eu sei que é bastante conteúdo, portanto se restar alguma dúvida nesse processo, não hesite: você pode abrir um tópico no fórum.

Tento responder pessoalmente sempre que possível. Quando não consigo, temos uma vasta comunidade de alunos, moderadores, instrutores e, com certeza, alguém vai conseguir te ajudar.

Em alguns momentos desse treinamento, farei referências a outras linguagens, principalmente C++ porque, além de ser compilada, já falei bastante sobre ela por aqui.

Se em algum momento essas referências não estiverem claras, não fizerem muito sentido, você pode aproveitar não só para usar o fórum e sanar suas dúvidas mas também dar uma olhada nos cursos que temos. Pelo menos para mim, ter estudado C++ ajudou bastante a entender o Rust.

Enfim, chega de falação, vamos ao próximo vídeo para colocar a mão na massa!

Arrays - Múltiplos valores

Bem-vindos de volta. Antes de começarmos a colocar a mão na massa, vamos só relembrar qual é o nosso ponto de partida. No final do último treinamento, criamos um projeto novo, vazio, utilizando o Cargo. É esse o projeto que vamos utilizar agora. Por enquanto, ele só tem uma função main com um simples Hello World.

Também configuramos o Visual Studio Code ou, caso você utilize algum editor diferente, você viu alguns plugins para ele. Então, já conseguimos compilar e rodar o nosso código sem precisar abrir um terminal, compilar o arquivo e depois executá-lo. Dessa forma, com um atalho do teclado, já rodamos o programa.

Agora, vamos imaginar que estamos desenvolvendo um sistema acadêmico, por exemplo, e precisamos armazenar todas as notas de uma pessoa. No caso, uma pessoa tem quatro notas durante o ano, correspondentes aos quatro bimestres do ensino no Brasil.

Então, vamos supor que, para armazenar essas quatro notas, precisamos toda vez criar quatro variáveis diferentes. Criaríamos, por exemplo, uma variável nota1 e, como é um valor muito pequeno e não pode ser negativo, utilizaríamos o unsigned de 8 bits e ficaria let nota1: u8 = 10;.

Então, digamos que a nota 1 foi 10 (let nota1: u8 = 10;); a nota 2 foi 8 (let nota2: u8 = 8;), a nota 3 foi 9.5... Já notamos que não podemos usar o unsigned, porque temos números flutuantes. Portanto, vamos mudar. É interessante usar o mesmo tipo em todas notas, então teremos que modificar todos para f32 manualmente! Não é muito conveniente:

fn main() {
    let nota1: f32 = 10;
    let nota2: f32 = 8;
    let nota3: f32 = 9.5
}

Nas duas primeiras notas, como estou utilizando números inteiros e os atribuindo a variáveis do tipo float, o que preciso fazer? Temos algumas opções, a mais simples é adicionar o tipo depois dela:

fn main() {
    let nota1: f32 = 10f32;
    let nota2: f32 = 8f32;
    let nota3: f32 = 9.5;
}

Quando adiciono o tipo depois do valor, é feito um cast, ou seja, armazenamos o valor do jeito que ele é, mas ocupando o espaço do outro tipo que especificamos (no caso, f32), sem modificação — é isso que o cast faz. Dessa forma, podemos fazer cast de tipos em Rust.

Continuando, a nota 4 será 6 (let nota4: f32 = 6f32;). Outra opção seria adicionar um ponto: let nota4: f32 = 6.0;.

Assim, com o println!(), posso exibir todas as notas. Vamos fazer a quebra de linha para não ficar ruim de ler, deixando cada parâmetro em uma linha:

fn main() {
    let nota1: f32 = 10f32;
    let nota2: f32 = 8f32;
    let nota3: f32 = 9.5;
    let nota4: f32 = 6.0;

    println!(
        "Nota1 = {}, Nota2 = {}, Nota3 = {}, Nota4 = {}", 
        nota1, 
        nota2, 
        nota3, 
        nota4
    );
}

Vai ficar extenso, mas estará mais legível. Então, vamos executar, não esperamos nenhuma surpresa, serão exibidas todas as notas.

Com essa abordagem, temos vários problemas. Se quisermos percorrer todas essas notas, precisaremos manualmente selecionar cada uma delas — não conseguimos percorrer de forma dinâmica, por exemplo, utilizando um loop ou estrutura parecida.

Se precisarmos mudar o tipo delas, será necessário ir em cada uma das definições e alterá-las. Se não soubermos quantas notas temos definidas, precisaremos contar uma a uma — não há um modo automatizado de checar quantas variáveis começam com o nome "nota", por exemplo.

Então, do jeito que está, não é intuitivo. Para armazenarmos conjuntos, listas ou dados relacionados juntos, podemos utilizar um array.

Geralmente, traduzimos esse termo para "vetor", em português, por causa dos vetores matemáticos. Neste curso, tentaremos chamá-los sempre de arrays, porque é a terminologia utilizada no Rust e, mais adiante neste treinamento, perceberemos que há uma chance de confundirmos com uma outra estrutura.

Então, vamos criar um array, existem algumas formas diferentes de fazer isso. Vamos declarar uma variável notas e informar as notas entre colchetes: let notas = [10f32, 8f32, 9.5, 6.0]. Note que continuamos indicando que alguns valores são floats de 32 bits. Assim, utilizamos todas as notas em uma única variável.

Podemos apagar as outras variáveis nota1, nota2, nota3 e nota4. Agora, no println!(), em vez de chamar variáveis diferentes, vou utilizar notas em todas elas. Porém notas é uma lista de dados, é um array de dados, um conjunto. Como selecionamos só a primeira, a segunda, a terceira?

Assim como em várias ou praticamente todas as linguagens de programação, usaremos a notação notas[0]. Começando do 0, selecionamos a primeira nota; no índice 1, escolhemos a segunda nota; no índice 2, a terceira, e assim em diante:

fn main() {
    let notas = [10f32, 8f32, 9.5, 6.0];

    println!(
        "Nota1 = {}, Nota2 = {}, Nota3 = {}, Nota4 = {}", 
        notas[0], 
        notas[1], 
        notas[2], 
        notas[3]
    );
}

Ao exibir, tenho o mesmo resultado. Além disso, também poderíamos utilizar um loop para percorrer.

Agora que já entendemos o que é o array e como o declaramos, vamos entender qual é a definição de tipo. Em [10f32, 8f32, 9.5, 6.0] deixamos que o próprio Rust definisse qual é o tipo da variável notas. Passando o mouse sobre notas, veremos [f32; 4]. Trata-se de um array de f32 — ou seja, as variáveis são do tipo float, utilizando 32 bits, porque foi o que especificamos — e temos 4 valores.

É importante ressaltar que, em um array, sempre sabemos o tamanho dele, sempre sabemos quantos itens o array pode ter, isto é, a sua capacidade. Se quisermos deixar essa indicação explícita, podemos declarar assim: let notas: [f32; 4] = [10f32, 8f32, 9.5, 6.0];.

Por exemplo, se removermos essa informação e colocarmos let notas = [10.0, 8.0, 9.5, 6.0];, quando passamos o mouse sobre notas, será mostrado [f64; 4]. O Rust interpretou que as variáveis são float, mas por garantia, por não saber o tamanho dos números que vamos ter, ele optou por um float de 64 bits.

De novo, se quisermos mudar os tipos, podemos modificar de uma vez só: let notas: [f32; 4] = [10f32, 8f32, 9.5, 6.0];. Então, dessa forma, declaramos os valores de um array e também conseguimos declarar o tipo dele.

No próximo vídeo, vamos entender um pouco sobre como manipular esse array e o que podemos fazer com ele.

Arrays - Manipulando arrays

Bem-vindos de volta. Então, vamos ver algumas formas que podemos fazer para manipular esse array e brincar um pouco com ele. Primeiro, como posso percorrê-lo de forma dinâmica, em vez de acessar item por item?

Podemos remover todo o println!() e vamos utilizar algum loop, por exemplo, o for, feito justamente para percorrer elementos dentro de algum conjunto, algum iterador, como chamamos. Por padrão, arrays já possuem um iterador. Então isso vai ser bem fácil.

Posso fazer um for nota in notas. Ou seja, quero pegar cada uma das notas dentro de notas. E posso exibi-las com o println!():

fn main() {
    let notas: [f32; 4] = [10.0, 8.0, 9.5, 6.0];

    for nota in notas {
        println!("A nota é = {}", nota);
    }
}

Repare que, dentro do array de notas, com um simples for consigo pegar cada um dos itens. Vou tentar executar e ver se não digitei nada errado. Está lá, a nota é 10, 8, 9.5, 6.

Outra forma de fazer isso, ainda com o for, é pegar cada um dos índices, ou seja, contar de 0 até 3 que é o último índice. Ou seja, 0, 1, 2, 3 (os meus quatro itens) e pegar esse índice para podermos exibir, por exemplo, "A nota 1 é 10, a nota 2 é 8".

Então para fazermos isso, vou fazer um for indice in 0..4, ou seja, vou percorrer o índice e não a nota em si, dentro de um intervalo de 0 até o tamanho desse array, que sabemos ser 4. Então, eu poderia fazer for indice in 0..4, mas não estaria muito dinâmico.

Para buscar o tamanho de um array, fazemos notas.len(). Dessa forma, tenho acesso ao tamanho de um array de forma dinâmica. "Vinicius, mas sempre sabemos o tamanho de um array?" Sim, só que notas.len() me permite mais flexibilidade, se amanhã ou depois eu precisar mudar esse array, adicionar mais itens, consigo continuar usando esse for.

Agora, posso utilizar println!("A nota {} é {}", nota) para dizer, por exemplo, que a nota 1 é 10. Só que a minha variável nota não existe mais. Então vamos adaptar.

Primeiro indice + 1, porque o índice começa com 0, então no índice 0 temos a nota 1. Depois notas[indice], para eu acessar a nota efetivamente, acessando o meu vetor notas no índice que peguei dentro desse for:

fn main() {
    let notas: [f32; 4] = [10.0, 8.0, 9.5, 6.0];

    for indice in 0..notas.len() {
        println!("A nota {} é = {}", indice + 1, notas[indice]);
    }
}

Repare que é por isso que essa sintaxe exclui o último número, o intervalo de 0 a 4 vai pegar os índices 0, 1, 2 e 3 justamente para podermos pegar as estruturas de dados que começam com 0. Então aqui não precisaria fazer algo como 0.=notas.len() -1 ou colocar parênteses, dificultar. É bem mais simples. Ele simplesmente exclui o último número para não precisarmos subtrair do valor que normalmente já utilizamos.

Ao executar, temos "A nota 1 é 10, a nota 2 é 8" e assim em diante. Um outro detalhe é que, às vezes, quero criar um array e ele pode ser muito grande, por exemplo, quero ter 10 notas. Só que quero inicializar todas com 6.5. Para não ter que digitar 6.5 nas quatro posições, posso utilizar uma sintaxe interessante.

No valor, vou dizer que quero valor 6.5, só que quero esse valor quatro vezes. Quero um array de quatro elementos com o valor 6.5: let notas: [f32; 4] = [6.5; 4];. Então repare que isso está do lado direito do operador de atribuição. Ou seja, [6.5; 4] não é um tipo, é o valor, ele criar um array de quatro elementos, todos eles com o valor 6.5.

Ao executar, todas as minhas notas vão ser 6.5. Aqui já começamos a manipular e brincar um pouco com arrays. Agora, imagine que eu queira, por exemplo, ter arrays multidimensionais. Quero ter uma matriz, como normalmente traduzimos.

Então, vamos fazer uma função de matriz. Quero definir uma matriz, que vai ser um vetor de vetores, ou um array de arrayslet matriz = [[ ]]:

fn main() {
    let notas: [f32; 4] = [10.0, 8.0, 9.5, 6.0];

    for indice in 0..notas.len() {
        println!("A nota {} é = {}", indice + 1, notas[indice]);
    }

    matriz();
}

fn matriz() {
    let matriz = [[]];
}

E vou ter, por exemplo, duas posições: [0.0, 1.2, 0.1] e embaixo vou ter outros números, [1.3, 0.3, 1.4]:

// código anterior omitido
fn matriz() {
    let matriz = [
        [0.0, 1.2, 0.1],
        [1.3, 0.3, 1.4]
    ];
}

Então posso ter arrays de arrays e isso gera um array multidimensional ou, como falamos na matemática, uma matriz. Para percorrer essa matriz, posso utilizar o for de novo. Para cada uma das linhas dessa minha matriz (for linha in matriz), posso fazer um outro for pegando um item em si, para cada item na minha linha (for item in linha).

Repare que, para percorrer matrizes, preciso de um for aninhado:

// código anterior omitido
fn matriz() {
    let matriz = [
        [0.0, 1.2, 0.1],
        [1.3, 0.3, 1.4]
    ];

    for linha in matriz {
        for item in linha {
            println!("Item = {}", item);
        }
    }
}

Assim, será exibido: 0.0, 1.2. 0.1, depois 1.3, 0.3 e 1.4. Simples assim. Então deixa eu executar. E beleza, "Item = 0", 1.2, 0.1 e assim em diante.

E caso você esteja se perguntando qual é o tipo desse dado, ele é um vetor de vetor, [[f64 ; 3]; 2]. O vetor interior,[f64; 3], tem 3 itens porque é quanto cada uma de nossas linhas tem; e temos duas linhas. Então se eu quisesse definir isso de forma explícita, por exemplo, trocando o f64 pelo f32 para economizar um pouco de memória, posso fazer let matriz: [[f32; 3]; 2].

É um vetor de vetores e possui duas linhas. O tipo é f32 e cada um dos vetores tem três elementos. Repare que, sempre que trabalho com arrays, eu conheço o número de elementos.

Não consigo ter um array com valor dinâmico. Vamos conversar sobre esse assunto mais adiante. Mas já saiba que é bem importante deixarmos claro, bem definido.

Vamos executar de novo só para garantir que esse código não quebrou:

// código anterior omitido
fn matriz() {
    let matriz: [[f32; 3]; 2] = [
        [0.0, 1.2, 0.1],
        [1.3, 0.3, 1.4]
    ];

    for linha in matriz {
        for item in linha {
            println!("Item = {}", item);
        }
    }
}

Agora quero voltar lá para a parte do código em que temos o notas e trazer um detalhe interessante para vocês.

Imagine que tenha um inteiro qualquer, por exemplo, let inteiro = 0; e quero exibi-lo. Inclusive, vou informar que ele é um unsigned de 8 bits: let inteiro: u8 = 0;. Para deixar mais padrão, vamos dizer assim, um signed de 64 bits, let inteiro: i64 = 0;. Vou ocupar bastante memória.

No println!(), vou fazer de um valor que vai ser notas com esse inteiro como índice:

fn main() {
    let notas: [f32; 4] = [10.0, 8.0, 9.5, 6.0];
    let inteiro: i64 = 0;

    println!("{}", notas[inteiro);

    for indice in 0..notas.len() {
        println!("A nota {} é = {}", indice + 1, notas[indice]);
    }
}
// código posterior omitido

Então quero acessar o valor 0 das minhas notas. Repare que está alegando um erro. Quando executo, a mensagem não é amigável: "slice indices are of type usize". Vamos entender um pouco melhor essa mensagem de erro no próximo vídeo.

Sobre o curso Rust: aprenda mais sobre tipos

O curso Rust: aprenda mais sobre tipos possui 99 minutos de vídeos, em um total de 45 atividades. Gostou? Conheça nossos outros cursos de em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda acessando integralmente esse e outros cursos, comece hoje!

Plus

De
R$ 1.800
12X
R$109
à vista R$1.308
  • Acesso a TODOS os cursos da Alura

    Mais de 1500 cursos completamente atualizados, com novos lançamentos todas as semanas, emProgramação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.

  • Alura Challenges

    Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.

  • Alura Cases

    Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.

  • Certificado

    Emitimos certificados para atestar que você finalizou nossos cursos e formações.

Matricule-se

Pro

De
R$ 2.400
12X
R$149
à vista R$1.788
  • Acesso a TODOS os cursos da Alura

    Mais de 1500 cursos completamente atualizados, com novos lançamentos todas as semanas, emProgramação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.

  • Alura Challenges

    Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.

  • Alura Cases

    Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.

  • Certificado

    Emitimos certificados para atestar que você finalizou nossos cursos e formações.

  • Luri powered by ChatGPT

    Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com Luri até 100 mensagens por semana.

  • Alura Língua (incluindo curso Inglês para Devs)

    Estude a língua inglesa com um curso 100% focado em tecnologia e expanda seus horizontes profissionais.

Matricule-se
Conheça os Planos para Empresas

Acesso completo
durante 1 ano

Estude 24h/dia
onde e quando quiser

Novos cursos
todas as semanas