<h2>Entendendo a Biblioteca thiserror</h2>
<p>A biblioteca <code>thiserror</code> é um derive macro que simplifica significativamente a criação de tipos de erro customizados em Rust. Enquanto a forma tradicional de implementar a trait <code>std::error::Error</code> exige boilerplate code verboso, <code>thiserror</code> automatiza esse processo, deixando seu código mais legível e mantível. A biblioteca é particularmente valiosa em projetos que lidam com múltiplas fontes de erro, como aplicações web, CLIs e bibliotecas.</p>
<p>O objetivo principal é reduzir a fricção na criação de erros ergonômicos. Em vez de escrever manualmente implementações de <code>Display</code>, <code>From</code> e outras traits, você usa atributos declarativos que o derive macro processa em tempo de compilação. Isso segue a filosofia Rust de "segurança sem sacrificar expressividade".</p>
<h3>Por que não usar std::error::Error diretamente?</h3>
<p>Implementar manualmente <code>std::error::Error</code> exige boilerplate: implementar <code>Display</code>, <code>Debug</code>, <code>From</code> para conversão automática. Com <code>thiserror</code>, tudo fica em um único tipo com atributos descritivos.</p>
<pre><code class="language-rust">// Sem thiserror - verboso
use std::error::Error;
use std::fmt;
#[derive(Debug)]
enum MinhaErro {
IoError(std::io::Error),
ParseError(String),
}
impl fmt::Display for MinhaErro {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MinhaErro::IoError(e) => write!(f, "Erro de IO: {}", e),
MinhaErro::ParseError(msg) => write!(f, "Erro de parse: {}", msg),
}
}
}
impl Error for MinhaErro {}
impl From<std::io::Error> for MinhaErro {
fn from(err: std::io::Error) -> Self {
MinhaErro::IoError(err)
}
}</code></pre>
<p>Compare com <code>thiserror</code> — muito mais clean.</p>
<h2>Estrutura Básica e Sintaxe</h2>
<p>A biblioteca funciona através de atributos aplicados a enums ou structs. O atributo <code>#[error(...)]</code> define como cada variante será exibida quando convertida em string, enquanto <code>#[from]</code> gera automaticamente implementações de <code>From</code>.</p>
<pre><code class="language-rust">use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConfigError {
#[error("Arquivo não encontrado: {0}")]
FileNotFound(String),
#[error("Erro de parsing TOML: {0}")]
TomlError(#[from] toml::de::Error),
#[error("Valor inválido para '{key}': {value}")]
InvalidValue { key: String, value: String },
#[error("Erro de I/O")]
IoError(#[from] std::io::Error),
}</code></pre>
<p>Neste exemplo, <code>#[from]</code> permite conversão automática de <code>toml::de::Error</code> e <code>std::io::Error</code> para <code>ConfigError</code>. Quando você retorna <code>?</code> sobre esses tipos, a conversão acontece implicitamente. O atributo <code>#[error(...)]</code> define exatamente como cada erro será exibido ao usuário.</p>
<h3>Usando #[source] para Error Chaining</h3>
<p>O atributo <code>#[source]</code> é crucial para criar chains de erro que preservam a causa raiz. Isso permite que ferramentas de diagnóstico e logs rastreiem o caminho completo do erro.</p>
<pre><code class="language-rust">use thiserror::Error;
#[derive(Error, Debug)]
pub enum ProcessingError {
#[error("Falha ao processar arquivo")]
ProcessingFailed {
#[from]
source: std::io::Error,
},
#[error("Validação falhou: {message}")]
ValidationFailed {
message: String,
#[source]
cause: Box<dyn std::error::Error + Send + Sync>,
},
}</code></pre>
<p>O <code>#[source]</code> permite que bibliotecas como <code>anyhow</code> e <code>eyre</code> façam backtrace completo dos erros. Quando você imprime o erro com <code>.source()</code>, consegue navegar toda a cadeia.</p>
<h2>Padrões Avançados</h2>
<h3>Combinando com Result Type Alias</h3>
<p>Um padrão muito comum é criar um type alias <code>Result</code> para sua aplicação, eliminando a necessidade de sempre escrever <code>Result<T, SeuErro></code>.</p>
<pre><code class="language-rust">use thiserror::Error;
#[derive(Error, Debug)]
pub enum ApiError {
#[error("Requisição HTTP falhou: {0}")]
HttpError(#[from] reqwest::Error),
#[error("Resposta inválida JSON: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Servidor retornou status {code}: {message}")]
ServerError { code: u16, message: String },
}
pub type ApiResult<T> = Result<T, ApiError>;
// Uso muito mais limpo
pub async fn fetch_user(id: u64) -> ApiResult<User> {
let response = reqwest::get(&format!("https://api.example.com/users/{}", id)).await?;
let user = response.json::<User>().await?;
Ok(user)
}</code></pre>
<p>Agora <code>ApiResult<T></code> é o idioma padrão do seu código, reduzindo ruído visual e tornando assinaturas de função mais expressivas.</p>
<h3>Erros Transparentes com #[error]</h3>
<p>Para casos onde você quer que um tipo de erro simplesmente passe através com a mensagem do erro subjacente, use <code>#[error(transparent)]</code>.</p>
<pre><code class="language-rust">use thiserror::Error;
#[derive(Error, Debug)]
#[error(transparent)]
pub struct ParseError(#[from] std::num::ParseIntError);
// Agora ParseError se comporta exatamente como ParseIntError,
// apenas adicionando segurança de tipo
fn parse_number(s: &str) -> Result<i32, ParseError> {
Ok(s.parse()?)
}</code></pre>
<h2>Conclusão</h2>
<p>A biblioteca <code>thiserror</code> resolve um problema real de ergonomia em Rust: criação de erros customizados sem boilerplate excessivo. Três aprendizados principais: <strong>primeiro</strong>, o derive macro <code>#[derive(Error)]</code> + atributo <code>#[error(...)]</code> elimina a necessidade de implementar <code>Display</code> manualmente; <strong>segundo</strong>, <code>#[from]</code> fornece conversão automática entre tipos de erro, permitindo usar <code>?</code> livremente; <strong>terceiro</strong>, <code>#[source]</code> preserva a cadeia de erro para debugging eficaz, especialmente importante em aplicações production-grade.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://docs.rs/thiserror/latest/thiserror/" target="_blank" rel="noopener noreferrer">Documentação Oficial thiserror</a></li>
<li><a href="https://doc.rust-lang.org/book/ch09-00-error-handling.html" target="_blank" rel="noopener noreferrer">Rust Book - Error Handling</a></li>
<li><a href="https://docs.rs/anyhow/latest/anyhow/" target="_blank" rel="noopener noreferrer">anyhow - Flexible concrete Error type</a></li>
<li><a href="https://www.oreilly.com/library/view/effective-rust/9781098109288/" target="_blank" rel="noopener noreferrer">Effective Rust - Error Handling</a></li>
<li><a href="https://github.com/rust-lang/rustlings" target="_blank" rel="noopener noreferrer">Rustlings Exercises</a></li>
</ul>