Convertendo Tipos de Erro

use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::fs::{self, File};
use std::io::{self, Read};

#[derive(Debug)]
enum LerNomeUsuarioErro {
    ErroEs(io::Error),
    NomeUsuarioVazio(String),
}

impl Error for LerNomeUsuarioErro {}

impl Display for LerNomeUsuarioErro {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self {
            Self::ErroEs(e) => write!(f, "Erro E/S: {e}"),
            Self::NomeUsuarioVazio(nome_arquivo) => write!(f, "Nome de usuário não encontrado em {nome_arquivo}"),
        }
    }
}

impl From<io::Error> for LerNomeUsuarioErro {
    fn from(err: io::Error) -> LerNomeUsuarioErro {
        LerNomeUsuarioErro::ErroEs(err)
    }
}

fn ler_nome_usuario(caminho: &str) -> Result<String, LerNomeUsuarioErro> {
    let mut nome_usuario = String::with_capacity(100);
    File::open(caminho)?.read_to_string(&mut nome_usuario)?;
    if nome_usuario.is_empty() {
        return Err(LerNomeUsuarioErro::NomeUsuarioVazio(String::from(caminho)));
    }
    Ok(nome_usuario)
}

fn main() {
    //fs::write("config.dat", "").unwrap();
    let nome_usuario = ler_nome_usuario("config.dat");
    println!("nome_usuario ou erro: {nome_usuario:?}");
}

Pontos chave:

  • A variável nome_usuario pode ser Ok(string) ou Err(error).
  • Use a chamada fs::write para testar os diferentes cenários: nenhum arquivo, arquivo vazio e arquivo com nome de usuário.

É uma boa prática para todos os tipos de erro que não precisam ser no_std implementar std::error::Error, que requer Debug e Display. O crate Error para core só está disponível em nightly, então ainda não é totalmente compatível com no_std.

Geralmente é útil para eles implementar Clone e Eq também quando possível, para tornar mais fácil a vida para testes e consumidores da sua biblioteca. Neste caso, não podemos fazê-lo facilmente, porque io::Error não os implementa.