🎯 Construção de um Conversor de Escalas Termométricas em Rust

Em poucas palavras

Aplicar conceitos de Segurança de Tipos e Encapsulamento para criar uma ferramenta de conversão entre Celsius, Fahrenheit e Kelvin, focando em um código que previne erros em tempo de compilação.


📝 Notas e Desenvolvimento


🔗 Conexões e Contexto


📚 Referências e Fontes

Citações Diretas

📂 Outros Conteúdos Preservados

🌡️ Projeto: Conversor de Temperatura em Rust

Objetivo

Aplicar conceitos de Segurança de Tipos e Encapsulamento para criar uma ferramenta de conversão entre Celsius, Fahrenheit e Kelvin, focando em um código que previne erros em tempo de compilação.


🛠️ 1. Contexto e Desafio

A conversão de temperaturas é um excelente exercício para dominar a manipulação de fórmulas e o controle de fluxo. Em Rust, elevamos este desafio utilizando estruturas que garantem que o programa nunca entre em um estado inválido.

O Desafio: Criar uma CLI (Interface de Linha de Comando) que solicite um valor e permita ao usuário escolher livremente as escalas de origem e destino.


📋 2. Requisitos do Projeto

Funcionais (O que o pRograma fAz)

  1. Entrada Robusta: O usuário deve digitar o valor. Se digitar letras, o programa deve avisar e solicitar novamente (sem travar).
  2. Menus Dinâmicos: Seleção clara para escala de Origem e escala de Destino.
  3. Precisão: Exibição do resultado com duas casas decimais e sua respectiva unidade.

Técnicos (Como o Rust dEve sEr uSado)

  • Enums: Para representar as escalas, garantindo que não existam “opções fantasmas”.
  • Blocos impl: Para anexar a lógica de conversão e formatação diretamente ao tipo de dado.
  • Pattern Matching: Para resolver as fórmulas matemáticas de forma exaustiva e elegante.
  • Prevenção de Panic: Substituir .expect() ou .unwrap() por tratamentos de erro dentro de loops.

💻 3. Implementação Sugerida

Abaixo, a solução idiomática que separa a interface do usuário (IO) da lógica de negócio (Matemática).

use std::io::{self, Write};
 
/// Define as escalas termométricas suportadas.
/// Derivamos Copy e Clone para facilitar a passagem da escala entre funções.
#[derive(Debug, Clone, Copy, PartialEq)]
enum TemperatureScale {
    Celsius,
    Fahrenheit,
    Kelvin,
}
 
impl TemperatureScale {
    /// Realiza a conversão baseada na escala atual (self) e na escala de destino (target).
    fn convert_to(&self, value: f64, target: TemperatureScale) -> f64 {
        if *self == target { return value; }
 
        match (self, target) {
            (Self::Celsius, Self::Fahrenheit) => (value * 9.0 / 5.0) + 32.0,
            (Self::Celsius, Self::Kelvin)     => value + 273.15,
            (Self::Fahrenheit, Self::Celsius) => (value - 32.0) * 5.0 / 9.0,
            (Self::Fahrenheit, Self::Kelvin)  => (value - 32.0) * 5.0 / 9.0 + 273.15,
            (Self::Kelvin, Self::Celsius)     => value - 273.15,
            (Self::Kelvin, Self::Fahrenheit)  => (value - 273.15) * 9.0 / 5.0 + 32.0,
            _ => unreachable!(),
        }
    }
 
    /// Retorna a representação textual da unidade.
    fn unit(&self) -> &str {
        match self {
            Self::Celsius => "°C",
            Self::Fahrenheit => "°F",
            Self::Kelvin => "K",
        }
    }
}
 
fn main() {
    println!("--- Conversor de Temperatura Profissional ---");
 
    let value = get_value_from_user();
    
    println!("\nSelecione a escala de ORIGEM:");
    let from_scale = get_choice_from_user();
    
    println!("\nSelecione a escala de DESTINO:");
    let to_scale = get_choice_from_user();
 
    let result = from_scale.convert_to(value, to_scale);
 
    println!(
        "\n✅ Resultado: {:.2}{} = {:.2}{}",
        value, from_scale.unit(), result, to_scale.unit()
    );
}
 
fn get_value_from_user() -> f64 {
    loop {
        print!("Digite o valor da temperatura: ");
        io::stdout().flush().unwrap();
        let mut input = String::new();
        io::stdin().read_line(&mut input).ok();
        
        match input.trim().parse::<f64>() {
            Ok(num) => return num,
            Err(_) => println!("❌ Erro: Digite um número válido (ex: 36.5)."),
        }
    }
}
 
fn get_choice_from_user() -> TemperatureScale {
    loop {
        print!("1. Celsius | 2. Fahrenheit | 3. Kelvin: ");
        io::stdout().flush().unwrap();
        let mut input = String::new();
        io::stdin().read_line(&mut input).ok();
 
        match input.trim() {
            "1" => return TemperatureScale::Celsius,
            "2" => return TemperatureScale::Fahrenheit,
            "3" => return TemperatureScale::Kelvin,
            _   => println!("⚠️ Escolha inválida."),
        }
    }
}

🧠 4. Conceitos Aprendidos

ConceitoAplicação neste projeto
impl BlockCentraliza as fórmulas matemáticas dentro do contexto do Enum.
Exhaustive MatchingO Rust garante que você não esqueceu nenhuma combinação de conversão (ex: Kelvin para Fahrenheit).
Zero-Cost AbstractionsO uso de Enums e métodos não deixa o programa mais lento que usar if/else manuais, mas o torna muito mais seguro.
io::stdout().flush()Essencial para garantir que prompts de texto apareçam antes da entrada do usuário em ambientes de terminal.

Dica de Ouro: Observe como o método convert_to recebe target: TemperatureScale. Isso permite que você expanda o programa para 10 escalas diferentes e a lógica de chamada na main continuará sendo exatamente uma única linha!