Curso gratuito de Rust: A linguagem mais amada
Variáveis, Mutabilidade e Tipos de Dados Básicos
Aprenda sobre variáveis, mutabilidade e tipos de dados básicos
🚀 Primeiros Passos com Rust: Variáveis, Mutabilidade e Tipos de Dados Básicos
Bem-vindos à primeira aula do módulo "Primeiros Passos com Rust"! 👋 Nesta aula, vamos desvendar alguns dos pilares fundamentais da linguagem Rust: como armazenar dados usando variáveis, entender o conceito de mutabilidade e explorar os tipos de dados básicos que Rust oferece. Esses conceitos são cruciais para escrever código seguro, eficiente e expressivo em Rust.
🎯 1. Introdução
Rust é conhecida por sua segurança e desempenho, e grande parte disso vem de como ela lida com dados. Ao contrário de muitas outras linguagens, Rust tem um modelo de propriedade e empréstimo rigoroso, que começa com a forma como você declara e interage com suas variáveis.
Nesta aula, você aprenderá:
- Como declarar variáveis em Rust.
- O que significa a imutabilidade por padrão e como torná-las mutáveis.
- A diferença entre variáveis e constantes.
- Os tipos de dados fundamentais para números, texto e valores booleanos.
- Como Rust infere tipos e quando você precisa especificá-los.
Vamos mergulhar! 🏊♂️
🧠 2. Explicação Detalhada com Exemplos
2.1. Variáveis e Imutabilidade por Padrão
Em Rust, as variáveis são declaradas usando a palavra-chave let. Uma característica fundamental e distintiva de Rust é que as variáveis são imutáveis por padrão. Isso significa que, uma vez que um valor é atribuído a uma variável, ele não pode ser alterado.
fn main() {
let x = 5; // Declara uma variável imutável 'x' com valor 5
println!("O valor de x é: {}", x);
// x = 6; // Isso causaria um erro de compilação!
// ^^^^^^^ cannot assign twice to immutable variable `x`
}A imutabilidade por padrão ajuda a prevenir bugs, especialmente em código concorrente, tornando mais fácil raciocinar sobre o estado do seu programa.
2.2. Mutabilidade: mut
Se você realmente precisa que uma variável seja alterável, você pode torná-la mutável adicionando a palavra-chave mut antes do nome da variável.
fn main() {
let mut x = 5; // Declara uma variável mutável 'x' com valor 5
println!("O valor de x é: {}", x);
x = 6; // Agora podemos alterar o valor de x!
println!("O novo valor de x é: {}", x);
}👉 Dica: Use mut apenas quando for estritamente necessário. O design de Rust encoraja a imutabilidade para maior segurança e clareza.
2.3. Shadowing (Sombreamento)
Rust permite que você declare uma nova variável com o mesmo nome de uma variável anterior. Isso é chamado de shadowing (sombreamento). A nova variável "sombreia" a anterior, o que significa que, a partir do ponto de declaração, o nome da variável se refere à nova variável. A variável original ainda existe, mas não pode ser acessada pelo nome.
Uma vantagem do shadowing é que você pode mudar o tipo de uma variável e reutilizar o nome, o que não é possível com mut.
fn main() {
let x = 5; // x é um inteiro
let x = x + 1; // x é sombreado, agora com valor 6
{
let x = x * 2; // Dentro deste escopo, x é sombreado novamente, agora com valor 12
println!("O valor de x no escopo interno é: {}", x); // Saída: 12
}
println!("O valor de x no escopo externo é: {}", x); // Saída: 6 (a variável interna foi descartada)
let spaces = " "; // spaces é uma string slice
let spaces = spaces.len(); // spaces é sombreado, agora com o comprimento da string (usize)
println!("O número de espaços é: {}", spaces);
}2.4. Constantes
Constantes são valores que são vinculados a um nome e não podem ser alterados. Elas são diferentes das variáveis mutáveis e imutáveis em algumas maneiras:
- Você usa a palavra-chave
constem vez delet. - O tipo da constante deve ser anotado explicitamente.
- Constantes podem ser declaradas em qualquer escopo, incluindo o escopo global.
- Elas só podem ser definidas como um resultado de uma expressão constante, não de um valor que só pode ser calculado em tempo de execução.
- Por convenção, os nomes de constantes são em
SCREAMING_SNAKE_CASE(todas as letras maiúsculas com underscores).
const MAX_POINTS: u32 = 100_000; // Declara uma constante do tipo u32
fn main() {
println!("O máximo de pontos permitido é: {}", MAX_POINTS);
// MAX_POINTS = 10; // Isso causaria um erro de compilação! Constantes não podem ser alteradas.
}2.5. Tipos de Dados Básicos
Rust é uma linguagem estaticamente tipada, o que significa que ela deve saber os tipos de todas as variáveis em tempo de compilação. No entanto, o compilador Rust geralmente pode inferir o tipo que você pretende usar com base no valor e como você o usa. Quando a inferência é ambígua, você deve anotar o tipo explicitamente.
Rust possui dois subconjuntos principais de tipos de dados: escalares e compostos.
2.5.1. Tipos Escalares
Um tipo escalar representa um único valor. Rust tem quatro tipos escalares primários: inteiros, números de ponto flutuante, booleanos e caracteres.
a) Inteiros
Inteiros são números sem uma parte fracionária. Eles podem ser assinados (podem ser positivos ou negativos) ou não assinados (apenas positivos).
| Comprimento | Assinado (i) | Não Assinado (u) |
|---|---|---|
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| arch | isize | usize |
isizeeusizedependem da arquitetura do seu computador (64 bits em sistemas de 64 bits, 32 bits em sistemas de 32 bits). Eles são usados principalmente para indexar coleções.- O tipo inteiro padrão em Rust é
i32. - Você pode usar underscores (
_) para melhorar a legibilidade de números longos, como1_000_000.
fn main() {
let decimal = 98_222; // i32 (inferido)
let hex = 0xff; // i32 (inferido)
let octal = 0o77; // i32 (inferido)
let binary = 0b1111_0000; // i32 (inferido)
let byte = b'A'; // u8 (para bytes ASCII, tipo explícito)
let i_32: i32 = -42; // Tipo explícito
let u_64: u64 = 1_000_000_000_000; // Tipo explícito
println!("Decimal: {}", decimal);
println!("Hex: {}", hex);
println!("Octal: {}", octal);
println!("Binário: {}", binary);
println!("Byte: {}", byte);
println!("i32: {}", i_32);
println!("u64: {}", u_64);
}b) Ponto Flutuante
Rust tem dois tipos de ponto flutuante:
f32(precisão simples)f64(precisão dupla) - este é o tipo padrão.
fn main() {
let x = 2.0; // f64 (inferido)
let y: f32 = 3.0; // f32 (tipo explícito)
println!("x (f64): {}", x);
println!("y (f32): {}", y);
}c) Booleanos
O tipo booleano em Rust tem dois valores possíveis: true e false. Ele ocupa um byte de memória.
fn main() {
let t = true;
let f: bool = false; // com anotação explícita de tipo
println!("Verdadeiro: {}", t);
println!("Falso: {}", f);
}d) Caracteres (char)
O tipo char em Rust representa um único caractere Unicode Scalar Value. Isso significa que ele pode representar muito mais do que apenas caracteres ASCII. char usa quatro bytes de memória.
fn main() {
let c = 'z';
let z: char = 'ℤ'; // com anotação explícita de tipo
let heart_eyed_cat = '😻';
println!("Caractere: {}", c);
println!("Caractere Unicode: {}", z);
println!("Emoji: {}", heart_eyed_cat);
}2.5.2. Tipos Compostos
Tipos compostos podem agrupar vários valores em um único tipo. Rust tem dois tipos compostos primitivos: tuplas e arrays.
a) Tuplas
Uma tupla é uma forma geral de agrupar alguns valores com uma variedade de tipos em um único tipo composto. As tuplas têm um comprimento fixo: uma vez declaradas, elas não podem crescer ou encolher.
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1); // Tupla com anotação de tipo explícita
// Acessando elementos da tupla por desestruturação
let (x, y, z) = tup;
println!("Os valores da tupla são: x={}, y={}, z={}", x, y, z);
// Acessando elementos da tupla por índice (começando em 0)
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = tup.2;
println!("Elementos individuais: {}, {}, {}", five_hundred, six_point_four, one);
}b) Arrays
Ao contrário de uma tupla, cada elemento em um array deve ter o mesmo tipo. Arrays também têm um comprimento fixo.
Arrays são úteis quando você sabe que sua lista de itens não mudará de tamanho. Quando você precisa de uma coleção de itens que pode mudar de tamanho, use um Vector (que veremos em aulas futuras).
fn main() {
let a = [1, 2, 3, 4, 5]; // Array inferido de i32
// Array com anotação de tipo e tamanho explícitos
let b: [i32; 5] = [1, 2, 3, 4, 5];
// Array com todos os elementos iguais (5 elementos, todos com valor 3)
let c = [3; 5]; // Equivalente a [3, 3, 3, 3, 3]
println!("Primeiro elemento de a: {}", a[0]);
println!("Terceiro elemento de b: {}", b[2]);
println!("Array c: {:?}", c); // Usamos {:?} para imprimir arrays
// Acessando um índice fora dos limites causaria um panic em tempo de execução!
// let index = 10;
// let element = a[index]; // Isso compila, mas falha em tempo de execução!
// println!("Elemento: {}", element);
}Rust garante que você não acesse acidentalmente um índice fora dos limites em tempo de execução, o que é uma das suas vantagens de segurança. Se você tentar acessar um índice inválido, o programa irá panic e encerrar.
📚 3. Código de Exemplo Oficial
Os exemplos acima são baseados e adaptados da documentação oficial do Rust, especificamente do livro "The Rust Programming Language" (Edição 2018), Capítulos 3.1 e 3.2. Você pode encontrá-los em:
É altamente recomendável consultar a documentação oficial para aprofundar seu conhecimento! 📖
📝 4. Resumo e Próximos Passos
Nesta aula, cobrimos os conceitos fundamentais de variáveis, mutabilidade e tipos de dados em Rust:
- Variáveis são declaradas com
lete são imutáveis por padrão. - Use
mutpara criar variáveis mutáveis. - Shadowing permite re-declarar variáveis com o mesmo nome e até mesmo mudar seu tipo.
- Constantes são valores imutáveis e globais, declarados com
conste exigem anotação de tipo. - Rust tem tipos escalares (inteiros, ponto flutuante, booleanos, caracteres) e compostos (tuplas, arrays).
- Rust infere tipos, mas você pode anotá-los explicitamente quando necessário.
Compreender esses conceitos é a base para escrever qualquer programa em Rust. Eles são cruciais para a segurança de memória e a performance que Rust oferece.
No próximo módulo, vamos explorar como as funções são definidas e usadas em Rust, e como elas se integram com esses tipos de dados! 🚀
Próximos Passos:
- Revise os conceitos de imutabilidade e mutabilidade.
- Experimente diferentes tipos de dados no seu ambiente Rust (
cargo run). - Leia os capítulos correspondentes no livro oficial do Rust para uma compreensão ainda mais aprofundada.
Até a próxima aula! 👋