Curso gratuito de Rust: A linguagem mais amada
Revisão de Conceitos Essenciais para o Projeto
Aprenda sobre revisão de conceitos essenciais para o projeto
Revisão de Conceitos Essenciais para o Projeto
Olá, futuro Rustacean! 👋 Nesta aula, faremos uma revisão rápida e focada dos conceitos fundamentais de Rust que serão a espinha dorsal do nosso projeto: o Jogo de Adivinhação. Não se preocupe, não vamos mergulhar em cada detalhe, mas sim relembrar o essencial para que você se sinta confiante ao começar a codificar.
Nosso objetivo é garantir que você tenha clareza sobre como usar variáveis, lidar com entrada e saída, controlar o fluxo do programa e até mesmo gerar números aleatórios. Preparado(a)? Vamos lá! 🚀
1. Variáveis e Mutabilidade (let, mut)
Em Rust, as variáveis são declaradas com a palavra-chave let. Por padrão, elas são imutáveis, o que significa que seu valor não pode ser alterado após a atribuição inicial. No entanto, para muitas situações (como um contador ou a entrada do usuário que será modificada), precisamos de variáveis mutáveis. Para isso, usamos a palavra-chave mut.
Por que isso é importante para o Jogo de Adivinhação?
Precisaremos de uma variável para armazenar o número secreto (que não mudará) e outra para o palpite do usuário (que mudará a cada tentativa).
Exemplo Oficial
fn main() {
// Variável imutável
let numero_secreto = 42;
println!("Número secreto (imutável): {}", numero_secreto);
// numero_secreto = 50; // Isso causaria um erro de compilação!
// Variável mutável
let mut palpite = String::new(); // Inicializamos com uma String vazia
println!("Palpite inicial (mutável): '{}'", palpite);
palpite.push_str("25"); // Podemos modificar a String
println!("Palpite modificado: '{}'", palpite);
}Saída:
Número secreto (imutável): 42
Palpite inicial (mutável): ''
Palpite modificado: '25'
2. Tipos de Dados e Conversão (String, u32, .trim(), .parse())
Rust é uma linguagem com tipagem estática, o que significa que o tipo de cada variável deve ser conhecido em tempo de compilação. Para o nosso jogo, os tipos principais que encontraremos são:
String: Usado para texto, especialmente para a entrada do usuário.u32: Um tipo de inteiro sem sinal de 32 bits, ideal para números positivos como o nosso palpite ou o número secreto.
Entrada do Usuário e Conversão
Quando lemos a entrada do usuário do terminal, ela sempre vem como uma String. Para comparar essa String com um número, precisamos convertê-la para um tipo numérico (como u32).
Para isso, usamos:
.trim(): Remove quaisquer espaços em branco no início e no final da string, incluindo o caractere de nova linha (\n) queread_linegeralmente captura..parse(): Tenta converter a string para um tipo numérico. Este método retorna umResult, pois a conversão pode falhar (por exemplo, se o usuário digitar "abc" em vez de um número).
Por que isso é importante para o Jogo de Adivinhação?
O usuário digitará um palpite como texto, e precisamos transformá-lo em um número para fazer a comparação com o número secreto.
Exemplo Oficial
use std::io; // Precisamos importar o módulo io para entrada/saída
fn main() {
println!("Digite um número:");
let mut entrada_usuario = String::new();
io::stdin()
.read_line(&mut entrada_usuario) // Lê a linha e a coloca em entrada_usuario
.expect("Falha ao ler a linha"); // Trata um possível erro de I/O
// Converte a String para u32
let numero: u32 = entrada_usuario
.trim() // Remove espaços em branco e a quebra de linha
.parse() // Tenta converter para u32
.expect("Por favor, digite um número!"); // Trata o erro se a conversão falhar
println!("Você digitou o número: {}", numero);
// Exemplo de como o parse retorna um Result e como podemos lidar com ele de forma mais robusta
match "123".parse::<u32>() {
Ok(num) => println!("Parse bem-sucedido: {}", num),
Err(e) => println!("Erro no parse: {}", e),
}
match "abc".parse::<u32>() {
Ok(num) => println!("Parse bem-sucedido: {}", num),
Err(e) => println!("Erro no parse: {}", e),
}
}Saída (exemplo, dependendo da entrada):
Digite um número:
42
Você digitou o número: 42
Parse bem-sucedido: 123
Erro no parse: invalid digit found in string
3. Entrada e Saída Padrão (std::io, io::stdin().read_line(), println!)
A interação com o usuário é fundamental em qualquer jogo. Em Rust, usamos o módulo std::io para lidar com a entrada e saída padrão (terminal).
io::stdin(): Retorna um handle para a entrada padrão do terminal..read_line(&mut buffer): Lê uma linha de texto da entrada padrão e a anexa aobuffer(umaStringmutável).println!: Uma macro poderosa para imprimir texto no console, aceitando argumentos formatados.
Por que isso é importante para o Jogo de Adivinhação?
Precisamos pedir ao usuário para digitar um palpite e, em seguida, informar se o palpite foi muito alto, muito baixo ou correto.
Exemplo Oficial
use std::io; // Importamos o módulo io
fn main() {
// Imprime uma mensagem para o usuário
println!("Adivinhe o número!");
println!("Por favor, digite seu palpite.");
let mut palpite = String::new(); // Variável mutável para armazenar a entrada
io::stdin()
.read_line(&mut palpite) // Lê a linha do terminal e armazena em 'palpite'
.expect("Falha ao ler a linha"); // Trata o erro, se houver
// Imprime o palpite do usuário
println!("Você palpitou: {}", palpite);
}Saída (exemplo):
Adivinhe o número!
Por favor, digite seu palpite.
50
Você palpitou: 50
4. Fluxo de Controle (if, else if, else, match, Ordering)
Para que nosso jogo seja interativo, precisamos tomar decisões com base no palpite do usuário. Rust oferece estruturas de controle de fluxo para isso.
if/else if/else: Para condições simples.match: Uma estrutura poderosa para comparar um valor com uma série de padrões. É frequentemente usada para tratar oResultde operações que podem falhar e para a enumeraçãoOrdering.Ordering: Uma enumeração do módulostd::cmpque representa o resultado de uma comparação:Less,GreaterouEqual.
Por que isso é importante para o Jogo de Adivinhação?
Precisamos comparar o palpite do usuário com o número secreto e dar feedback adequado ("Muito baixo!", "Muito alto!", "Você acertou!").
Exemplo Oficial
use std::cmp::Ordering; // Importamos a enumeração Ordering
fn main() {
let numero_secreto = 75;
let palpite = 75; // Vamos simular um palpite
// Usando if/else if/else
if palpite < numero_secreto {
println!("Seu palpite é muito baixo!");
} else if palpite > numero_secreto {
println!("Seu palpite é muito alto!");
} else {
println!("Você acertou!");
}
// Usando match com Ordering (mais idiomático para comparações)
match palpite.cmp(&numero_secreto) { // .cmp() retorna um Ordering
Ordering::Less => println!("Muito baixo! (via match)"),
Ordering::Greater => println!("Muito alto! (via match)"),
Ordering::Equal => println!("Você acertou! (via match)"),
}
}Saída:
Você acertou!
Você acertou! (via match)
5. Loops (loop, break)
Um jogo de adivinhação geralmente permite múltiplas tentativas até que o usuário acerte o número. Para isso, precisamos de um loop que continue executando até que uma condição seja satisfeita.
loop: Cria um loop infinito que continuará executando seu bloco de código repetidamente.break: Usado para sair de um loop.
Por que isso é importante para o Jogo de Adivinhação?
O jogo precisa continuar pedindo palpites até que o usuário adivinhe o número secreto corretamente.
Exemplo Oficial
fn main() {
let numero_secreto = 42;
let mut tentativas = 0;
println!("Tente adivinhar o número secreto (entre 1 e 100).");
loop { // Loop infinito
tentativas += 1;
println!("Tentativa #{}: Digite seu palpite:", tentativas);
let palpite = 42; // Simulação: vamos fingir que o usuário digitou 42
// let palpite = 50; // Se fosse 50, o loop continuaria
if palpite == numero_secreto {
println!("Parabéns! Você acertou o número secreto em {} tentativas!", tentativas);
break; // Sai do loop quando o palpite está correto
} else if palpite < numero_secreto {
println!("Muito baixo!");
} else {
println!("Muito alto!");
}
}
}Saída (com palpite = 42):
Tente adivinhar o número secreto (entre 1 e 100).
Tentativa #1: Digite seu palpite:
Parabéns! Você acertou o número secreto em 1 tentativas!
6. Geração de Números Aleatórios (Crate rand)
Para tornar nosso jogo divertido, o número secreto precisa ser diferente a cada vez que o jogo é executado. Rust não possui uma funcionalidade de número aleatório em sua biblioteca padrão, mas a comunidade Rust fornece uma crate excelente para isso: rand.
Adicionando uma Dependência Externa
Para usar uma crate externa, você precisa adicioná-la ao seu arquivo Cargo.toml na seção [dependencies].
# Cargo.toml
[dependencies]
rand = "0.8.5" # Use a versão mais recente disponívelDepois de salvar o Cargo.toml, o Cargo fará o download e compilará a crate rand para você.
Usando a Crate rand
A crate rand oferece a funcionalidade Rng (gerador de números aleatórios) e o método gen_range(start..=end) para gerar um número dentro de um intervalo inclusivo.
Por que isso é importante para o Jogo de Adivinhação?
É como o nome diz: precisamos de um número secreto e aleatório!
Exemplo Oficial
// main.rs
use rand::Rng; // Importamos o trait Rng
fn main() {
// Cria um gerador de números aleatórios thread-local
let mut rng = rand::thread_rng();
// Gera um número aleatório entre 1 e 100 (inclusivo)
let numero_secreto = rng.gen_range(1..=100);
println!("O número secreto é: {}", numero_secreto); // Para fins de depuração
}Saída (exemplo, pois é aleatório):
O número secreto é: 67
7. Integração de Conceitos
No nosso projeto do Jogo de Adivinhação, todos esses conceitos se unem de forma orgânica. Não há "múltiplas tecnologias" no sentido de frameworks diferentes, mas sim diferentes partes da linguagem Rust e uma crate externa que trabalham em conjunto para construir a lógica do jogo.
- Você usará
let mutpara o palpite do usuário. - Você usará
randpara gerar onumero_secreto. - Você usará
println!para interagir com o usuário eio::stdin().read_line()para obter a entrada. - Você usará
.trim().parse()para converter a entrada do usuário deStringparau32. - Você usará um
looppara permitir várias tentativas. - Você usará
matchcomOrderingpara comparar o palpite com o número secreto e decidir se o jogo continua ou termina.
Exercícios de Revisão 🧠
Para consolidar seu entendimento, tente responder às seguintes perguntas (sem olhar o código se possível!):
- Qual a diferença fundamental entre
let x = 5;elet mut x = 5;? Quando você usaria cada um no jogo de adivinhação? - Por que precisamos usar
.trim()e.parse()na entrada do usuário? O que aconteceria se não os usássemos? - Qual é a principal vantagem de usar um
matchcomOrderingem vez de múltiplosif/else ifpara comparar o palpite com o número secreto? - Como você faria para que o jogo de adivinhação continuasse pedindo palpites até que o usuário acertasse o número? Qual palavra-chave você usaria para sair desse ciclo?
- Se você quisesse que o número secreto fosse entre 1 e 1000 (inclusivo), como você modificaria o código da crate
rand?
Resumo e Próximos Passos 🚀
Nesta aula, revisamos os pilares do Rust que serão cruciais para o nosso Jogo de Adivinhação:
- Variáveis e Mutabilidade:
letpara imutáveis,let mutpara mutáveis. - Tipos e Conversão:
Stringpara entrada,u32para números, e como.trim().parse()converte entre eles. - I/O:
std::ioparaprintln!eread_line. - Fluxo de Controle:
if/elseematchcomOrderingpara tomar decisões. - Loops:
looppara repetição ebreakpara sair. - Números Aleatórios: A crate
randpara gerar o número secreto.
Com essa revisão em mente, você está mais do que preparado(a) para a próxima etapa! Na próxima aula, vamos colocar a mão na massa e começar a construir o nosso Jogo de Adivinhação passo a passo, aplicando todos esses conceitos na prática.
Até lá! Mantenha o foco e a curiosidade! ✨