Rust - Tratamento de Erros: Como Evitar Panic com Result e Match
Fala galera, no último post eu disse que iríamos conectar ao banco de dados utilizando a crate SQLx
, porém é necessário falarmos de tratamento de erros, pois fazer conexão no banco de dados pode ocasionar erros como banco de dados não encontrado, falha nas permissões, entre outros. Então, hoje iremos falar o básico de tratamento de erros em Rust.
Digamos que queremos escrever em um arquivo. Para isso, poderíamos escrever uma função parecida com essa:
use std::{fs::File, io::Write};
fn write_to_file() {
let mut file = File::create("example.txt").expect("Falha ao criar o arquivo");
file.write_all(b"Hello, Rust!").expect("Falha ao escrever no arquivo");
}
Essa função irá criar um arquivo na pasta do projeto com o nome example.txt
e o texto Hello, Rust!
. Execute o programa e você verá o arquivo sendo criado e o seu conteúdo. Porém, se alterarmos o local do arquivo para um que não existe ou que não temos permissão, o programa entrará em pânico e será fechado. Para testes, passe um local de arquivo que você sabe que não existe ou não tem permissão. Por exemplo: File::create("/root/example.txt")
. Como estou no Linux, verei o seguinte erro:
thread 'main' panicked at src/main.rs:32:54:
Falha ao criar o arquivo: Os { code: 13, kind: PermissionDenied, message: "Permission denied" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
No meu caso, ele indica que houve um erro na linha 32:54, justamente em File::create("/root/example.txt")
, onde passamos o caminho onde não temos permissão. Para resolver isso, podemos modificar a função para retornar um Result
, que é um enumerador que podemos utilizar para representar o resultado de uma operação que pode falhar. Ele tem a seguinte estrutura:
enum Result<T, E> {
Ok(T),
Err(E),
}
Onde Ok(T)
informa o valor de sucesso e o Err(E)
informa o valor do erro. Alterando a nossa função para retornar o Result
, ela ficará assim:
use std::{fs::File, io::{self, Write}};
fn write_to_file() -> Result<(), io::Error> {
let mut file = match File::create("/root/example.txt") {
Ok(f) => f,
Err(e) => return Err(e),
};
match file.write_all(b"Hello, Rust!") {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
Aqui resolvemos o problema do pânico utilizando a expressão match
para verificar se podemos criar o arquivo. Se for possível, guardamos o retorno em Ok
; se não, retornamos o erro em Err
e a função é terminada. Caso seja Ok
, vamos para o segundo match
, que verifica se conseguimos salvar no arquivo, utilizando a mesma regra do match
anterior.
Para executar essa função, mudamos a chamada na função main
:
if let Err(e) = write_to_file() {
eprintln!("Erro: {}", e);
}
Caso haja falha, pedimos para imprimir o erro:
Erro: Permission denied (os error 13)
Nesse caso, poderíamos tentar resolver de alguma forma ou enviar uma mensagem para o chamador, sem finalizar o sistema de forma inesperada.
Mas existe uma forma de deixar a função menos verbosa usando o operador ?
. Quando você tem uma função que pode retornar em mais de um lugar, mas o retorno do Err
for sempre o mesmo, você poderá utilizar o operador ?
. Exemplo:
fn write_to_file() -> Result<(), io::Error> {
let mut file = File::create("/root/example.txt")?;
file.write_all(b"Hello, Rust!")?;
Ok(())
}
Note como a função ficou mais enxuta. Em cada verificação, terminamos com o operador ?
, pois, caso falhem, todos retornam o tipo io::Error
. Caso tudo funcione corretamente, é retornado Ok
. Essa função tem a mesma chamada na função main
que a versão anterior.
Quando utilizamos Result
, podemos verificar se a função ocorreu com sucesso ou erro utilizando is_ok
ou is_err
. Exemplo:
let test = write_to_file();
if test.is_ok() {
println!("Arquivo escrito com sucesso.");
}
if test.is_err() {
eprintln!("Erro ao escrever no arquivo.");
}
Creio que isso cobre o básico sobre tratamento de erros. Quando formos utilizar o SQLx, precisamos saber se houve erros na conexão com o banco, por exemplo, e tratá-los sem que o sistema entre em pânico e finalize de forma inesperada.
Valeu por acompanhar até aqui! Qualquer dúvida, deixa nos comentários. Fique com Deus!
🔗 Link para a postagem no Linkedin