Seu primeiro programa
por Sutton AshfordEntenda o conceito
Todo programa Rust executável começa pela função main. É ali que o computador inicia a execução, seguindo as instruções de cima para baixo. Para compilar um arquivo isolado, use rustc ola.rs e depois rode o executável gerado (./ola); em projetos de verdade, o cargo run faz as duas coisas.
Para escrever na tela, usamos println!. O ponto de exclamação indica que ela é uma macro, não uma função comum: o compilador confere o texto e os argumentos em tempo de compilação. println! pula linha no fim; print! não pula.
O texto aceita marcadores {}, trocados pelos argumentos que vêm depois dele. Também é possível indicar posição ({0}), nome ({total}) e formato, como largura, alinhamento e casas decimais ({:>8.2}). Para imprimir uma chave de verdade, dobre-a: {{ e }}.
- A execução começa em
fn main(); cada instrução termina com;. println!imprime e pula linha;print!imprime sem pular;eprintln!escreve na saída de erro.- Cada
{}recebe um argumento, e a quantidade precisa bater, senão o código não compila. - Depois dos dois-pontos vem o formato:
{:<10}alinha à esquerda,{:>8.2}à direita com duas casas,{:-^20}centraliza preenchendo com-. - Texto fica entre aspas duplas; aspas simples são para um único caractere (
char).
Do conceito à prática
3 exemplos para ver a ideia funcionando.
01Avisos da biblioteca com println!
// Todo programa Rust executável começa pela função main.
fn main() {
// println! é uma macro (repare no "!"): imprime o texto e pula linha.
println!("Bem-vindo à Biblioteca Municipal!");
println!("Horário: segunda a sexta, das 8h às 18h.");
// Uma linha vazia também é uma chamada válida.
println!();
// Cada instrução termina com ponto e vírgula.
println!("Empréstimos: até 3 livros por vez.");
println!("Prazo de devolução: 14 dias.");
// Comentários de linha começam com // e são ignorados pelo compilador.
println!("Boa leitura!");
}
O exemplo mostra o esqueleto mínimo de um programa Rust: a função main e uma sequência de chamadas a println!, cada uma gerando uma linha. A chamada println!() sem argumentos imprime só a quebra de linha.
Como main executa as instruções na ordem em que aparecem, o texto na tela segue exatamente a ordem do código. Compile com rustc avisos.rs e rode ./avisos.
02Cardápio com placeholders e print!
// Placeholders {} recebem os argumentos que vêm depois do texto.
fn main() {
println!("=== Cardápio do dia ===");
// Cada {} é trocado pelo próximo argumento, na ordem.
println!("{}: {} porções", "Lasanha", 8);
println!("{}: {} porções", "Salada", 4);
// Também dá para indicar a posição: {0} e {1} podem se repetir.
println!("{0} leva {1} ovos; dobrando a receita, {0} leva {2}.", "Bolo", 3, 6);
// Argumentos com nome deixam o texto mais legível.
println!("Tempo de forno: {minutos} minutos a {graus} graus.", minutos = 40, graus = 180);
// print! não pula linha: as duas chamadas formam uma linha só.
print!("Sobremesa: ");
println!("pudim");
// Para imprimir chaves de verdade, dobre-as: {{ e }}.
println!("Ingredientes secretos: {{ não revelados }}");
// \t insere uma tabulação e \" imprime aspas dentro do texto.
println!("Dica\t\"sirva gelado\"");
}
Aqui o texto deixa de ser fixo: os marcadores {} recebem os argumentos na ordem, {0} e {1} escolhem pela posição (e podem se repetir) e {minutos} usa um argumento com nome. O print! mostra como montar uma linha em partes.
O exemplo também traz os escapes mais usados: {{ e }} para chaves literais, \t para tabulação e \" para aspas dentro do texto.
03Recibo da loja com largura e casas decimais
// Formatação de largura, alinhamento e casas decimais para montar um recibo.
fn main() {
// {:-<30} preenche com "-" até 30 colunas; o argumento vazio vira só a linha.
println!("{:-<30}", "");
// {:^30} centraliza o texto em 30 colunas.
println!("{:^30}", "LOJA DO BAIRRO");
println!("{:-<30}", "");
// {:<14} alinha à esquerda; {:>3} e {:>13.2} alinham à direita.
// O .2 fixa duas casas decimais no número.
println!("{:<14}{:>3}{:>13}", "Produto", "Qtd", "Preço");
println!("{:<14}{:>3}{:>13.2}", "Caderno", 2, 25.9);
println!("{:<14}{:>3}{:>13.2}", "Caneta azul", 5, 12.5);
println!("{:<14}{:>3}{:>13.2}", "Mochila", 1, 149.0);
println!("{:-<30}", "");
// O argumento pode ser uma conta: ela é calculada antes de imprimir.
println!("{:<17}{:>13.2}", "Total", 25.9 + 12.5 + 149.0);
// {:05} completa com zeros à esquerda até 5 dígitos.
println!("Pedido nº {:05}", 42);
// eprintln! escreve na saída de erro (stderr), não na saída normal.
eprintln!("aviso: recibo gerado em modo de teste");
}
O recibo usa a especificação de formato depois dos dois-pontos: <, ^ e > escolhem o alinhamento, o número define a largura e .2 fixa duas casas decimais. Com as mesmas larguras em todas as linhas, as colunas ficam alinhadas sem contar espaços à mão.
O total é uma expressão calculada no próprio argumento, e {:05} completa o número do pedido com zeros. A última linha usa eprintln!, que escreve na saída de erro: ela aparece no terminal, mas não vai para um arquivo se você redirecionar só a saída normal (./recibo > recibo.txt).
Agora é com você
Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.
01Tela inicial do jogoFácil
Escreva um programa que mostre a tela inicial de um jogo de aventura: o título entre duas molduras de asteriscos, uma linha em branco e um menu com três opções, exatamente como na saída esperada.
- O programa tem a função
maine usa sóprintln!para imprimir. - Cada linha da saída vem de uma chamada própria a
println!. - A linha em branco é feita com
println!(), sem argumentos. - A saída é idêntica à esperada, inclusive os dois espaços antes do título.
fn main() {
println!("**********************");
// imprima o título, a moldura de baixo, a linha em branco e o menu
}
**********************
AVENTURA NA MASMORRA
**********************
1 - Novo jogo
2 - Continuar
3 - Sair
Ver dica
Espaços dentro das aspas fazem parte da saída, e println! sem argumentos ainda pula uma linha.
Ver resposta
fn main() {
// Moldura de cima
println!("**********************");
// Título do jogo e a instrução, uma chamada por linha
println!(" AVENTURA NA MASMORRA");
println!("**********************");
// Linha em branco separando o título do menu
println!();
println!("1 - Novo jogo");
println!("2 - Continuar");
println!("3 - Sair");
}
Passo a passo
fn main() { ... }declara a função por onde o programa começa; sem ela, orustcnão gera um executável.- As duas linhas de asteriscos são
println!com o texto literal: a macro imprime a string e acrescenta a quebra de linha no fim. - O título tem dois espaços no início dentro das aspas, porque espaços dentro da string fazem parte da saída.
println!();sem argumentos imprime só a quebra de linha, criando a linha em branco pedida entre o título e o menu.- As três opções do menu ficam em chamadas separadas, uma por linha, na ordem em que devem aparecer.
- Cada chamada termina com
;, que encerra a instrução em Rust.
Por que funciona
Um programa Rust executa as instruções de main de cima para baixo. Como cada println! escreve uma linha completa, a ordem das chamadas no código é exatamente a ordem das linhas na tela. O ponto de exclamação indica que println! é uma macro: o compilador confere a string em tempo de compilação e gera o código que escreve na saída padrão.
Erros comuns
- Esquecer o
!:println("Sair");não compila (expected function, found macro println). Escrevaprintln!. - Aspas simples:
println!('3 - Sair');falha, porque em Rust aspas simples são para um único caractere (char). Texto usa aspas duplas. - Esquecer o
;entre duas chamadas: o compilador acusaexpected ;. Termine cada instrução com ponto e vírgula. - Linha em branco com
println!("")e espaço dentro:println!(" ")imprime um espaço e a saída deixa de bater. Useprintln!().
Outra forma de resolver
Dá para usar uma única chamada com \n no meio do texto: println!("1 - Novo jogo\n2 - Continuar\n3 - Sair");. É útil para blocos fixos curtos, mas uma chamada por linha fica mais fácil de ler e de editar.
Saída esperada
**********************
AVENTURA NA MASMORRA
**********************
1 - Novo jogo
2 - Continuar
3 - Sair
02Placar da rodadaMédio
Mostre o placar de uma rodada de jogo usando marcadores de formatação em vez de escrever os números no texto. A linha com os pontos dos dois jogadores deve ser montada em partes, com print!.
- O número da rodada entra por um argumento nomeado (
{rodada}). - A linha do placar é montada com pelo menos duas chamadas a
print!e termina com umprintln!. - A última frase usa o marcador posicional
{0}duas vezes para repetir o nome da líder. - Nenhum nome ou número aparece escrito direto dentro das strings de formatação.
fn main() {
// cabeçalho com {rodada}
// placar: print! em partes e println! no fim
// frase final com {0} e {1}
}
Placar da rodada 3
Ana: 12 pontos | Bruno: 9 pontos
Ana lidera; Ana precisa de 3 pontos para vencer.
Ver dica
Pense em quais pedaços da linha do placar devem ficar na mesma linha: só a última chamada pode pular linha.
Ver resposta
fn main() {
// Cabeçalho com argumento nomeado
println!("Placar da rodada {rodada}", rodada = 3);
// print! monta a linha em partes; só o último println! pula linha
print!("{}: {} pontos", "Ana", 12);
print!(" | ");
println!("{}: {} pontos", "Bruno", 9);
// {0} reaparece sem repetir o argumento
println!("{0} lidera; {0} precisa de {1} pontos para vencer.", "Ana", 3);
}
Passo a passo
println!("Placar da rodada {rodada}", rodada = 3);usa um argumento nomeado: o{rodada}do texto é trocado pelo valor dado emrodada = 3.- O primeiro
print!imprimeAna: 12 pontossem pular linha; os dois{}recebem"Ana"e12, nessa ordem. - O segundo
print!acrescenta o separador" | "na mesma linha, por isso os espaços ficam dentro das aspas. - O
println!final completa a linha com os pontos do Bruno e só então pula para a próxima. - Na última frase,
{0}aparece duas vezes e usa o primeiro argumento ("Ana") nas duas;{1}pega o segundo (3). - Números e textos entram nos mesmos
{}: a macro sabe exibir os dois tipos.
Por que funciona
As macros de formatação percorrem o texto e trocam cada marcador pelo argumento correspondente: {} pega o próximo da fila, {0} e {1} escolhem pela posição e {nome} pelo nome. A diferença entre print! e println! é só a quebra de linha no fim, então várias chamadas print! seguidas de um println! montam uma única linha.
Erros comuns
- Quantidade errada de argumentos:
println!("{}: {} pontos", "Ana");não compila (2 positional arguments in format string, but there is 1 argument). Passe um argumento para cada{}. - Argumento sobrando:
println!("{} pontos", 12, 9);também é erro de compilação (argument never used). Rust não ignora valores extras. - Usar
println!no lugar do primeiroprint!: a linha do placar se quebra em três e a saída deixa de bater. Só a última parte usaprintln!. - Esquecer os espaços do separador:
print!("|")gerapontos|Bruno. Os espaços precisam estar dentro das aspas.
Outra forma de resolver
A linha do placar cabe numa chamada só: println!("{}: {} pontos | {}: {} pontos", "Ana", 12, "Bruno", 9);. Ela é preferível quando a linha inteira já é conhecida; o print! em partes serve quando cada pedaço vem de uma etapa diferente do programa.
Saída esperada
Placar da rodada 3
Ana: 12 pontos | Bruno: 9 pontos
Ana lidera; Ana precisa de 3 pontos para vencer.
03Boletim alinhado em colunasDesafio
Imprima o boletim de três alunos em colunas alinhadas de 28 caracteres no total, com título centralizado entre sinais de igual, notas com uma casa decimal e a média calculada no próprio println!.
- O título usa preenchimento e centralização (
{:=^28}) e a linha final usa{:=<28}, sem digitar os sinais=à mão. - O nome ocupa 12 colunas alinhado à esquerda; prova e média ocupam 8 colunas cada, à direita, com uma casa decimal.
- A média de cada aluno é a expressão
(prova + trabalho) / 2.0passada como argumento, com trabalho 7.5, 9.0 e 9.0. - A última linha mostra as chaves literais
{ todos aprovados }.
fn main() {
println!("{:=^28}", " BOLETIM 1º BIMESTRE ");
println!("{:<12}{:>8}{:>8}", "Aluno", "Prova", "Média");
// linhas de Carla (8.5), Diego (6.0) e Elisa (10.0)
// linha final e situação
}
=== BOLETIM 1º BIMESTRE ====
Aluno Prova Média
Carla 8.5 8.0
Diego 6.0 7.5
Elisa 10.0 9.5
============================
Situação: { todos aprovados }
Ver dica
Largura e precisão andam juntas no mesmo marcador, nesta ordem: alinhamento, largura e depois o ponto com as casas decimais.
Ver resposta
fn main() {
// Título centralizado com "=" preenchendo os dois lados até 28 colunas
println!("{:=^28}", " BOLETIM 1º BIMESTRE ");
// Cabeçalho: nome à esquerda em 12 colunas, notas à direita em 8
println!("{:<12}{:>8}{:>8}", "Aluno", "Prova", "Média");
// .1 fixa uma casa decimal; a média é calculada no próprio argumento
println!("{:<12}{:>8.1}{:>8.1}", "Carla", 8.5, (8.5 + 7.5) / 2.0);
println!("{:<12}{:>8.1}{:>8.1}", "Diego", 6.0, (6.0 + 9.0) / 2.0);
println!("{:<12}{:>8.1}{:>8.1}", "Elisa", 10.0, (10.0 + 9.0) / 2.0);
// Linha de 28 "=" feita com preenchimento, sem digitar os sinais
println!("{:=<28}", "");
// Chaves dobradas viram chaves literais
println!("Situação: {{ todos aprovados }}");
}
Passo a passo
{:=^28}centraliza o texto em 28 colunas e usa=como caractere de preenchimento; os espaços em volta do título ficam dentro da string para separá-lo dos sinais.- O cabeçalho usa
{:<12}para o nome (alinhado à esquerda) e{:>8}para as colunas de nota (à direita), somando as mesmas 28 colunas do título. - Nas linhas dos alunos,
{:>8.1}junta largura e precisão: o número ocupa 8 colunas, alinhado à direita, com exatamente uma casa decimal. - A média entra como expressão no próprio argumento,
(8.5 + 7.5) / 2.0: Rust calcula o valor antes de formatar. - Todas as notas são escritas com ponto decimal (
6.0,2.0), porque somar ou dividirf64com inteiro não compila. {:=<28}com o argumento""não tem texto para mostrar, então o preenchimento gera só a linha de 28 sinais=.- Na última linha,
{{e}}são a forma de escrever chaves literais dentro de uma string de formatação.
Por que funciona
Depois dos dois-pontos, o marcador aceita uma especificação: caractere de preenchimento, alinhamento (<, ^ ou >), largura e precisão. Como todas as linhas usam as mesmas larguras, as colunas ficam alinhadas sem contar espaços à mão. A largura conta caracteres, não bytes, por isso Média e 1º ocupam o mesmo espaço que letras sem acento.
Erros comuns
- Misturar inteiro e decimal:
(6.0 + 9) / 2não compila (cannot add {integer} to {float}). Escreva9.0e2.0. - Esquecer a precisão:
{:>8}com8.0imprime8, e a coluna perde a casa decimal. Use{:>8.1}. - Chave sozinha no texto:
println!("Situação: { todos aprovados }");não compila (invalid format string). Dobre as chaves:{{e}}. - Larguras que não somam 28: com
{:<10}no nome, as linhas ficam mais curtas que o título e a saída deixa de bater. Confira que 12 + 8 + 8 = 28.
Outra forma de resolver
A largura também pode vir de um argumento: println!("{:=<w$}", "", w = 28);. Isso é útil quando a mesma largura se repete em várias linhas e você quer mudá-la num lugar só; na próxima lição, com variáveis, essa ideia fica ainda mais prática.
Saída esperada
=== BOLETIM 1º BIMESTRE ====
Aluno Prova Média
Carla 8.5 8.0
Diego 6.0 7.5
Elisa 10.0 9.5
============================
Situação: { todos aprovados }