Sua dose diária de programação

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

✦   Lição

Operadores

Operadores são os símbolos que combinam valores: os aritméticos (+, -, *, /, %), os de comparação e os lógicos, que você já usou nas condicionais, os de atribuição composta (+=, -=, *=, /=, %=) e os bit a bit (&, |, ^, !, <<, >>). Rust não tem ++ nem --, e potência não é operador: usa-se o método pow (inteiros) ou powf (decimais).

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

Operadores

por Beckett Hollis
Iniciante

Entenda o conceito

Operadores são os símbolos que combinam valores: os aritméticos (+, -, *, /, %), os de comparação e os lógicos, que você já usou nas condicionais, os de atribuição composta (+=, -=, *=, /=, %=) e os bit a bit (&, |, ^, !, <<, >>). Rust não tem ++ nem --, e potência não é operador: usa-se o método pow (inteiros) ou powf (decimais).

Numa expressão com vários operadores, a precedência define a ordem: as vem antes de *, / e %, que vêm antes de + e -; depois vêm os deslocamentos e os bit a bit e, só então, as comparações. Por isso x & MASCARA == 0 em Rust significa (x & MASCARA) == 0. Na dúvida, parênteses deixam a intenção explícita.

Os dois lados de um operador precisam ter o mesmo tipo, e o resultado também tem esse tipo. Quando ele não cabe (um u8 passando de 255, por exemplo), há estouro: em modo debug o programa entra em pânico e em modo release o valor dá a volta. Para controlar isso, os inteiros têm métodos como checked_add, saturating_sub e wrapping_add. Com negativos, % mantém o sinal do dividendo, e rem_euclid devolve sempre um resto positivo.

↳ Pontos-chave
  • Rust não tem ++ nem --: use += e -=. Potência é método: 7_i32.pow(2) ou 2.0_f64.powf(0.5).
  • Precedência: as, depois * / %, depois + -, depois << >>, depois & ^ | e só então as comparações. Parênteses resolvem qualquer dúvida.
  • & testa e desliga bits, | liga, ^ alterna, ! inverte todos e <</>> deslocam; com uma constante por bit, um u8 guarda até oito opções.
  • Estouro de inteiro causa pânico em debug e dá a volta em release; checked_* devolve Option, saturating_* para no limite e wrapping_* dá a volta de propósito.
  • Com negativos, -7 % 3 dá -1; (-7_i32).rem_euclid(3) dá 2, útil para contas circulares como dias da semana.

Do conceito à prática

3 exemplos para ver a ideia funcionando.

01Média do boletim com precedência e parênteses

boletim.rs
// Operadores aritméticos, precedência e parênteses.
fn main() {
    let prova: f64 = 8.0;
    let trabalho: f64 = 6.5;
    let participacao: f64 = 9.0;

    // * e / vêm antes de + e -; os parênteses mudam a ordem.
    let sem_parenteses = prova + trabalho / 2.0;
    let com_parenteses = (prova + trabalho) / 2.0;
    println!("Sem parênteses: {sem_parenteses}");
    println!("Com parênteses: {com_parenteses}");

    // Média ponderada: pesos 5, 3 e 2.
    let media = (prova * 5.0 + trabalho * 3.0 + participacao * 2.0) / 10.0;
    println!("Média ponderada: {media:.2}");

    // Entre inteiros, / descarta a fração e % devolve o resto.
    let aulas: i32 = 75;
    let faltas: i32 = 11;
    let presenca = (aulas - faltas) * 100 / aulas;
    println!(
        "Presença: {presenca}% (resto {})",
        (aulas - faltas) * 100 % aulas
    );

    // Rust não tem ++ nem --: use += e -=.
    let mut pontos_extras = 0;
    pontos_extras += 1;
    pontos_extras *= 3;
    println!("Pontos extras: {pontos_extras}");

    // Potência é um método, não um operador.
    let quadrado = 7_i32.pow(2);
    let raiz = 2.0_f64.powf(0.5);
    println!("7² = {quadrado}, 2^0,5 = {raiz:.3}");

    // O - unário inverte o sinal.
    let ajuste = -0.5;
    println!("Média ajustada: {:.2}", media + ajuste);
}

As duas primeiras contas usam os mesmos números e dão resultados diferentes: sem parênteses, trabalho / 2.0 é calculado antes da soma (11.25); com parênteses, a soma vem primeiro (7.25). A média ponderada junta multiplicações e somas e só divide no fim, graças aos parênteses.

Com inteiros, (aulas - faltas) * 100 / aulas multiplica antes de dividir para não perder precisão, e % mostra o resto que a divisão inteira descartou. O exemplo também mostra que Rust troca ++ por += e *=, e que potência é feita com os métodos pow e powf, com o tipo do literal indicado por sufixo (7_i32, 2.0_f64).

02Habilidades do personagem com operadores bit a bit

permissoes.rs
// Operadores bit a bit: cada bit de um u8 guarda uma opção.
const MOVER: u8 = 1 << 0; // 0000_0001
const ATACAR: u8 = 1 << 1; // 0000_0010
const VOAR: u8 = 1 << 2; // 0000_0100
const NADAR: u8 = 1 << 3; // 0000_1000

fn main() {
    // | junta opções num único valor.
    let mut habilidades = MOVER | ATACAR;
    println!("Início:      {habilidades:08b}");

    // |= liga um bit sem mexer nos outros.
    habilidades |= NADAR;
    println!("Com nado:    {habilidades:08b}");

    // & testa se um bit está ligado.
    let pode_voar = habilidades & VOAR != 0;
    let pode_nadar = habilidades & NADAR != 0;
    println!("Voa? {pode_voar}, nada? {pode_nadar}");

    // &= com !bit desliga um bit (! inverte todos os bits do u8).
    habilidades &= !ATACAR;
    println!("Sem ataque:  {habilidades:08b}");

    // ^= alterna: liga se estava desligado e vice-versa.
    habilidades ^= VOAR;
    println!("Voo trocado: {habilidades:08b}");

    // count_ones conta quantos bits estão ligados.
    println!("Habilidades ativas: {}", habilidades.count_ones());

    // << e >> deslocam bits: multiplicam ou dividem por potências de 2.
    let moedas: u32 = 5;
    println!("{moedas} << 3 = {}, 40 >> 2 = {}", moedas << 3, 40_u32 >> 2);
}

Cada habilidade é uma constante com um único bit ligado, criada com 1 << n. O | junta várias num só u8, |= liga mais uma, & testa se uma está presente, &= !ATACAR desliga uma sem mexer nas outras e ^= alterna. O formato {habilidades:08b} imprime os oito bits para acompanhar cada mudança.

Repare em habilidades & VOAR != 0: em Rust, os operadores bit a bit têm precedência maior que as comparações, então a expressão significa (habilidades & VOAR) != 0. No fim, << e >> mostram que deslocar bits equivale a multiplicar ou dividir por potências de 2 (5 << 3 é 5 * 8).

03Estoque sem estouro com checked, saturating e wrapping

estoque.rs
// Estouro de inteiros: checked, saturating e wrapping.
fn main() {
    let estoque: u8 = 250;
    let chegada: u8 = 10;

    // checked_add devolve Some(valor) ou None quando o resultado não cabe.
    match estoque.checked_add(chegada) {
        Some(total) => println!("Novo estoque: {total}"),
        None => println!("Estouro: {estoque} + {chegada} não cabe em u8"),
    }

    // saturating_* para no limite do tipo em vez de estourar.
    println!("Saturando a soma: {}", estoque.saturating_add(chegada));
    let reservados: u8 = 3;
    println!("Saturando a subtração: {}", reservados.saturating_sub(5));

    // wrapping_* dá a volta, como um hodômetro.
    println!("Dando a volta: {}", estoque.wrapping_add(chegada));

    // checked_sub evita tirar mais do que existe.
    let pedido: u8 = 4;
    match reservados.checked_sub(pedido) {
        Some(resto) => println!("Sobram {resto} reservados"),
        None => println!("Pedido de {pedido} recusado: só há {reservados}"),
    }

    // Com negativos, % mantém o sinal; rem_euclid devolve sempre >= 0.
    let saldo: i32 = -7;
    println!("-7 % 3 = {}", saldo % 3);
    println!("(-7).rem_euclid(3) = {}", saldo.rem_euclid(3));
    println!(
        "-7 / 3 = {}, (-7).div_euclid(3) = {}",
        saldo / 3,
        saldo.div_euclid(3)
    );
}

Um u8 guarda no máximo 255, e 250 + 10 não cabe. Com + comum, o programa entraria em pânico em modo debug. Os métodos deixam a escolha explícita: checked_add devolve None e o match trata o caso, saturating_add para em 255 e wrapping_add dá a volta (260 - 256 = 4), como um hodômetro. O mesmo vale para a subtração com saturating_sub e checked_sub.

As últimas linhas mostram a divisão com negativos: % e / arredondam em direção a zero (-7 % 3 dá -1), enquanto rem_euclid e div_euclid mantêm o resto sempre positivo. Note os parênteses em (-7).rem_euclid(3): sem eles, o método seria aplicado ao 7 antes do sinal de menos.


Agora é com você

Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.

01Fatias de pizza na festaFácil

Numa festa há 3 pizzas de 8 fatias para 7 convidados. Mostre quantas fatias cada um recebe e quantas sobram; depois chegam mais 2 convidados e a divisão é refeita, terminando com a prova real da divisão.

↳ Requisitos
  • As quantidades são u32, e convidados é a única variável mut, aumentada com +=.
  • As fatias por pessoa usam / e as que sobram usam %.
  • A última linha imprime o bool de por_pessoa * convidados + sobra == total.
  • A saída é idêntica à esperada.
código inicial
fn main() {
    let pizzas: u32 = 3;
    let fatias_por_pizza: u32 = 8;
    let mut convidados: u32 = 7;

    // total de fatias, divisão para 7 convidados

    // chegam mais 2 convidados: refaça a divisão

    // prova real: quociente * divisor + resto == total
}
saída esperada
Total de fatias: 24
Para 7 convidados: 3 cada, sobram 3
Para 9 convidados: 2 cada, sobram 6
Conferência: true
Ver dica

A divisão entre inteiros já descarta a fração; o operador que devolve exatamente o que ela jogou fora é o que você precisa para as sobras.

Ver resposta
fn main() {
    let pizzas: u32 = 3;
    let fatias_por_pizza: u32 = 8;
    let mut convidados: u32 = 7;

    // * multiplica; / e % dividem entre inteiros
    let total = pizzas * fatias_por_pizza;
    println!("Total de fatias: {total}");
    println!(
        "Para {convidados} convidados: {} cada, sobram {}",
        total / convidados,
        total % convidados
    );

    // Rust não tem ++: += soma e guarda na mesma variável
    convidados += 2;
    let por_pessoa = total / convidados;
    let sobra = total % convidados;
    println!("Para {convidados} convidados: {por_pessoa} cada, sobram {sobra}");

    // Prova real da divisão: quociente * divisor + resto
    println!("Conferência: {}", por_pessoa * convidados + sobra == total);
}

Passo a passo

  1. As três variáveis são u32 porque fatias e pessoas nunca são negativas; convidados leva mut porque o número de convidados muda no meio do programa.
  2. pizzas * fatias_por_pizza calcula o total (24) uma vez só e guarda o resultado, em vez de repetir a multiplicação em cada conta.
  3. total / convidados é uma divisão inteira: 24 / 7 dá 3, e a fração é descartada; total % convidados devolve o que sobra dessa divisão (3).
  4. convidados += 2; é a forma curta de convidados = convidados + 2;. Rust não tem ++, então o operador composto é o jeito idiomático de somar.
  5. Com 9 convidados, a mesma dupla / e % dá 2 fatias para cada um e 6 de sobra.
  6. A última linha faz a prova real: por_pessoa * convidados + sobra == total. A multiplicação e a soma acontecem antes da comparação ==, que devolve um bool.

Por que funciona

Para inteiros, / e % são um par: o quociente diz quantas fatias inteiras cada pessoa recebe e o resto diz quantas ficam na travessa. Sempre vale quociente * divisor + resto == dividendo, e é isso que a conferência imprime como true. A precedência dos operadores garante que as contas aritméticas sejam feitas antes do ==, sem precisar de parênteses.

Erros comuns

  • Usar ++: convidados++; não compila (Rust has no postfix increment operator). Use convidados += 1; ou, aqui, convidados += 2;.
  • Esperar fração na divisão inteira: 24 / 9 dá 2, não 2.67. Se precisar das casas decimais, converta os dois lados com as f64 antes de dividir.
  • Esquecer o mut: sem let mut convidados, a linha convidados += 2; falha com cannot assign twice to immutable variable.
  • Dividir por zero: com convidados igual a 0, a divisão causa pânico (attempt to divide by zero). Quando o divisor puder ser zero, teste antes com if.

Outra forma de resolver

Em vez de / e % separados, dá para usar os métodos total.div_euclid(convidados) e total.rem_euclid(convidados). Para números positivos o resultado é o mesmo; eles são preferíveis quando os valores podem ser negativos, porque o resto nunca sai menor que zero.

Saída esperada

Total de fatias: 24
Para 7 convidados: 3 cada, sobram 3
Para 9 convidados: 2 cada, sobram 6
Conferência: true

02Selos de um produto da lojaMédio

Guarde os selos de um produto (frete grátis, promoção, esgotado e importado) como bits de um único u8 e acompanhe o produto do cadastro até a reposição, mostrando os bits a cada etapa e, no fim, se ele aparece na vitrine.

↳ Requisitos
  • Cada selo é uma constante u8 criada com 1 << n, na ordem: frete grátis, promoção, esgotado, importado.
  • A promoção liga dois selos com um único |=; ao esgotar, uma só expressão liga esgotado e desliga promoção e frete grátis usando |, & e !.
  • A reposição usa ^=, e as respostas finais usam & comparado com zero e count_ones().
  • Os bits são impressos com {selos:04b} e a saída é idêntica à esperada.
código inicial
const FRETE_GRATIS: u8 = 1 << 0;
const PROMOCAO: u8 = 1 << 1;
const ESGOTADO: u8 = 1 << 2;
const IMPORTADO: u8 = 1 << 3;

fn main() {
    let mut selos: u8 = IMPORTADO;
    println!("Cadastro:  {selos:04b}");

    // promoção, esgotou e reposição

    // na vitrine? frete grátis? quantos selos ativos?
}
saída esperada
Cadastro:  1000
Promoção:  1011
Esgotou:   1100
Reposição: 1000
Na vitrine? true, frete grátis? false
Selos ativos: 1
Ver dica

Para desligar bits sem tocar nos outros, combine & com uma máscara em que só os bits a apagar valem 0; o ! constrói essa máscara.

Ver resposta
// Um bit para cada selo do produto
const FRETE_GRATIS: u8 = 1 << 0;
const PROMOCAO: u8 = 1 << 1;
const ESGOTADO: u8 = 1 << 2;
const IMPORTADO: u8 = 1 << 3;

fn main() {
    let mut selos: u8 = IMPORTADO;
    println!("Cadastro:  {selos:04b}");

    // |= liga os dois selos de uma vez
    selos |= PROMOCAO | FRETE_GRATIS;
    println!("Promoção:  {selos:04b}");

    // Liga ESGOTADO e, com & e !, desliga promoção e frete grátis
    selos = (selos | ESGOTADO) & !(PROMOCAO | FRETE_GRATIS);
    println!("Esgotou:   {selos:04b}");

    // ^= inverte o bit: o produto volta ao estoque
    selos ^= ESGOTADO;
    println!("Reposição: {selos:04b}");

    // & isola um bit; != 0 transforma o resultado em bool
    let na_vitrine = selos & ESGOTADO == 0;
    let frete_gratis = selos & FRETE_GRATIS != 0;
    println!("Na vitrine? {na_vitrine}, frete grátis? {frete_gratis}");
    println!("Selos ativos: {}", selos.count_ones());
}

Passo a passo

  1. Cada constante liga um único bit com 1 << n: FRETE_GRATIS é 0001, PROMOCAO é 0010, ESGOTADO é 0100 e IMPORTADO é 1000. Assim, quatro selos cabem num só u8.
  2. selos |= PROMOCAO | FRETE_GRATIS; primeiro junta os dois selos com | (0011) e depois liga esses bits em selos, sem mexer no bit de importado.
  3. (selos | ESGOTADO) & !(PROMOCAO | FRETE_GRATIS) faz duas coisas: o | liga o bit de esgotado, e o & com a máscara invertida por ! desliga promoção e frete grátis.
  4. selos ^= ESGOTADO; inverte só aquele bit: como ele estava ligado, passa a desligado e o produto volta a ter só o selo de importado.
  5. selos & ESGOTADO == 0 isola o bit de esgotado e compara com zero. Em Rust, & tem precedência maior que ==, então a conta é feita antes da comparação.
  6. count_ones() conta os bits ligados; sobrou 1, o de importado.

Por que funciona

Os operadores bit a bit trabalham em cada posição do número separadamente. | liga bits (basta um lado ter 1), & mantém só os bits presentes nos dois lados, ! inverte todos e ^ inverte onde o outro lado tem 1. Com uma constante por bit, cada operação mexe só nos selos desejados e deixa os outros intactos, e {selos:04b} mostra o resultado em binário para conferir.

Erros comuns

  • Somar em vez de usar |: selos + PROMOCAO funciona quando o bit está desligado, mas, se ele já estiver ligado, a soma vai para o bit seguinte e liga o selo errado. Use | ou |=.
  • Desligar com ^: selos ^= PROMOCAO desliga só se o bit estiver ligado; se não estiver, ele liga. Para desligar com certeza, use selos &= !PROMOCAO.
  • Usar != sem o &: selos != ESGOTADO compara o número inteiro, não o bit. Com 1100, a expressão dá true mesmo com o produto esgotado. Isole o bit: selos & ESGOTADO != 0.

Outra forma de resolver

A passagem para esgotado também pode ser feita em dois comandos: selos |= ESGOTADO; e depois selos &= !(PROMOCAO | FRETE_GRATIS);. Fica mais fácil de ler quando cada passo tem um motivo diferente; a expressão única é preferível quando a mudança deve acontecer de uma vez, como uma única regra de negócio.

Saída esperada

Cadastro:  1000
Promoção:  1011
Esgotou:   1100
Reposição: 1000
Na vitrine? true, frete grátis? false
Selos ativos: 1

03Devolução atrasada na bibliotecaDesafio

Um leitor está há 17 dias com um livro cujo prazo é de 10 dias, e hoje é sexta. Descubra em que dia da semana ele retirou o livro e o dia do vencimento, calcule o atraso, desconte 3 pontos de fidelidade por dia sem deixar o saldo estourar e tente reservar 2 exemplares de um título que só tem 1.

↳ Requisitos
  • Os dias da semana são i32 (0 = domingo, 6 = sábado), e a retirada é calculada com rem_euclid(7), porque a subtração fica negativa; o vencimento usa %.
  • Os nomes dos dias saem de um array constante DIAS, indexado com as usize.
  • Os pontos (12) são u8, reduzidos com saturating_sub; a reserva usa checked_sub com um match sobre Some e None.
  • A saída é idêntica à esperada.
código inicial
const DIAS: [&str; 7] = [
    "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
];
const PRAZO_DIAS: i32 = 10;
const PENALIDADE_POR_DIA: u8 = 3;

fn main() {
    let hoje: i32 = 5;
    let dias_com_livro: i32 = 17;
    let pontos: u8 = 12;
    let exemplares: u8 = 1;
    let pedido_reserva: u8 = 2;

    // dia da retirada, vencimento e atraso

    // penalidade nos pontos sem estourar o u8

    // reserva com checked_sub
}
saída esperada
Hoje: sexta
Retirada: terça
Vencimento: sexta
Dias de atraso: 7
Pontos de fidelidade: 0 (perdeu 21)
Reserva de 2 recusada: só há 1
Ver dica

Calcule 5 - 17 e veja o que % faz com um número negativo; existe um método de resto que nunca devolve valor menor que zero.

Ver resposta
// Dias da semana: 0 = domingo, 6 = sábado
const DIAS: [&str; 7] = [
    "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
];
const PRAZO_DIAS: i32 = 10;
const PENALIDADE_POR_DIA: u8 = 3;

fn main() {
    let hoje: i32 = 5;
    let dias_com_livro: i32 = 17;
    let pontos: u8 = 12;
    let exemplares: u8 = 1;
    let pedido_reserva: u8 = 2;

    // 5 - 17 dá -12; rem_euclid devolve um dia válido de 0 a 6
    let retirada = (hoje - dias_com_livro).rem_euclid(7);
    // A soma é positiva, então % basta
    let vencimento = (retirada + PRAZO_DIAS) % 7;
    println!("Hoje: {}", DIAS[hoje as usize]);
    println!("Retirada: {}", DIAS[retirada as usize]);
    println!("Vencimento: {}", DIAS[vencimento as usize]);

    let atraso = dias_com_livro - PRAZO_DIAS;
    println!("Dias de atraso: {atraso}");

    // as vem antes de *: (atraso as u8) * PENALIDADE_POR_DIA
    let penalidade = atraso as u8 * PENALIDADE_POR_DIA;
    // saturating_sub para em 0 em vez de estourar o u8
    let restantes = pontos.saturating_sub(penalidade);
    println!("Pontos de fidelidade: {restantes} (perdeu {penalidade})");

    // checked_sub devolve None quando não há exemplares suficientes
    match exemplares.checked_sub(pedido_reserva) {
        Some(sobra) => println!("Reserva confirmada; sobram {sobra}"),
        None => println!("Reserva de {pedido_reserva} recusada: só há {exemplares}"),
    }
}

Passo a passo

  1. DIAS é um array constante com os sete nomes; um número de 0 a 6 convertido com as usize serve de índice (DIAS[5] é "sexta"). Os dias ficam em i32 porque a conta da retirada passa por um valor negativo.
  2. hoje - dias_com_livro dá -12. Com %, o resultado seria -5, que não é um dia válido; rem_euclid(7) devolve sempre um número de 0 a 6, aqui 2 (terça).
  3. (retirada + PRAZO_DIAS) % 7 soma o prazo e volta ao início da semana quando passa de sábado: (2 + 10) % 7 dá 5, sexta. Como a soma é positiva, % basta.
  4. atraso é a diferença entre os dias com o livro e o prazo: 17 - 10 dá 7.
  5. atraso as u8 * PENALIDADE_POR_DIA converte primeiro e multiplica depois, porque as tem precedência maior que *. A penalidade fica em 21 pontos.
  6. pontos.saturating_sub(penalidade) tira 21 de 12 sem estourar: o resultado para em 0, o menor valor de um u8.
  7. checked_sub devolve Some(resto) quando a subtração cabe no tipo e None quando não cabe. O match trata os dois casos, e a reserva de 2 exemplares é recusada porque só há 1.

Por que funciona

Cada operação escolhe o comportamento certo para o que pode dar errado. O resto euclidiano (rem_euclid) faz a aritmética circular dos dias funcionar também para trás no tempo. Nos pontos, perder mais do que se tem significa zerar, e é isso que a saturação faz. Na reserva, faltar exemplar é um caso de negócio que precisa de resposta, por isso checked_sub devolve um Option e o match obriga a tratar o None. Em nenhum ponto o programa depende do estouro silencioso de um inteiro.

Erros comuns

  • Usar % com negativos: (hoje - dias_com_livro) % 7 dá -5, e -5 as usize vira um número enorme; o acesso ao array causa pânico com index out of bounds. Use rem_euclid(7).
  • Subtrair u8 direto: pontos - penalidade com 12 e 21 causa pânico em modo debug (attempt to subtract with overflow) e, em modo release, dá a volta e devolve 247. Use saturating_sub ou checked_sub.
  • Chamar método num literal negativo: -12.rem_euclid(7) não faz o que parece, porque o método é aplicado antes do - unário; o compilador ainda reclama do tipo ambíguo (can't call method rem_euclid on ambiguous numeric type). Escreva (-12_i32).rem_euclid(7).
  • Ignorar o None: usar exemplares.checked_sub(pedido_reserva).unwrap() encerra o programa com pânico quando falta exemplar. Trate os dois casos com match.

Outra forma de resolver

A reserva poderia ser decidida com um if pedido_reserva <= exemplares { ... } else { ... } antes da subtração, e o - comum ficaria seguro dentro do ramo verdadeiro. Funciona bem quando a regra é simples; checked_sub é preferível quando a conta faz parte de um cálculo maior, porque a própria operação avisa que não coube e o compilador não deixa esquecer esse caso.

Saída esperada

Hoje: sexta
Retirada: terça
Vencimento: sexta
Dias de atraso: 7
Pontos de fidelidade: 0 (perdeu 21)
Reserva de 2 recusada: só há 1
← Anterior na trilha de RustDecisões com condicionais

Últimas lições

Ver o arquivo completo →