Sua dose diária de programação

Conceito, 3 exemplos e 3 exercícios: uma pequena conquista de cada vez.

✦   Lição

Seu primeiro programa

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.

Rust Fundamentos Iniciante 3 exemplos + 3 exercícios
42 lições publicadas 10 linguagens novas lições todo dia útil
Rust / Fundamentos

Seu primeiro programa

por Sutton Ashford
Iniciante

Entenda 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 }}.

↳ Pontos-chave
  • 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!

avisos.rs
// 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!

cardapio.rs
// 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

recibo.rs
// 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.

↳ Requisitos
  • O programa tem a função main e 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.
código inicial
fn main() {
    println!("**********************");
    // imprima o título, a moldura de baixo, a linha em branco e o menu
}
saída esperada
**********************
  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

  1. fn main() { ... } declara a função por onde o programa começa; sem ela, o rustc não gera um executável.
  2. 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.
  3. O título tem dois espaços no início dentro das aspas, porque espaços dentro da string fazem parte da saída.
  4. println!(); sem argumentos imprime só a quebra de linha, criando a linha em branco pedida entre o título e o menu.
  5. As três opções do menu ficam em chamadas separadas, uma por linha, na ordem em que devem aparecer.
  6. 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). Escreva println!.
  • 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 acusa expected ;. 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. Use println!().

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!.

↳ Requisitos
  • 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 um println!.
  • 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.
código inicial
fn main() {
    // cabeçalho com {rodada}

    // placar: print! em partes e println! no fim

    // frase final com {0} e {1}
}
saída esperada
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

  1. println!("Placar da rodada {rodada}", rodada = 3); usa um argumento nomeado: o {rodada} do texto é trocado pelo valor dado em rodada = 3.
  2. O primeiro print! imprime Ana: 12 pontos sem pular linha; os dois {} recebem "Ana" e 12, nessa ordem.
  3. O segundo print! acrescenta o separador " | " na mesma linha, por isso os espaços ficam dentro das aspas.
  4. O println! final completa a linha com os pontos do Bruno e só então pula para a próxima.
  5. Na última frase, {0} aparece duas vezes e usa o primeiro argumento ("Ana") nas duas; {1} pega o segundo (3).
  6. 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 primeiro print!: a linha do placar se quebra em três e a saída deixa de bater. Só a última parte usa println!.
  • Esquecer os espaços do separador: print!("|") gera pontos|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!.

↳ Requisitos
  • 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.0 passada como argumento, com trabalho 7.5, 9.0 e 9.0.
  • A última linha mostra as chaves literais { todos aprovados }.
código inicial
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
}
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 }
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

  1. {:=^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.
  2. 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.
  3. Nas linhas dos alunos, {:>8.1} junta largura e precisão: o número ocupa 8 colunas, alinhado à direita, com exatamente uma casa decimal.
  4. A média entra como expressão no próprio argumento, (8.5 + 7.5) / 2.0: Rust calcula o valor antes de formatar.
  5. Todas as notas são escritas com ponto decimal (6.0, 2.0), porque somar ou dividir f64 com inteiro não compila.
  6. {:=<28} com o argumento "" não tem texto para mostrar, então o preenchimento gera só a linha de 28 sinais =.
  7. 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) / 2 não compila (cannot add {integer} to {float}). Escreva 9.0 e 2.0.
  • Esquecer a precisão: {:>8} com 8.0 imprime 8, 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 }

Últimas lições

Ver o arquivo completo →