Como utilizar unwrap corretamente no Rust.
Se você já começou a estudar Rust e se deparou com funções unwrap
do tipo:
let any_number: u16 = unknown_var.parse().unwrap();
ou
let any_number: u16 = unknown_var.parse()?;
Saiba que há riscos no uso desenfreado desse tipo de código. unwrap
pode fazer com que o seu código entre em pânico
(entenda melhor lendo o capítulo 9 do livro), quando seu código entra nessa situação a aplicação começa o processo de "morte" e caso não tenha um processo de reload seu serviço será encerrado.
Mesmo que você implemente um processo de reexecutar o sistema caso ele seja morto, esse não é um comportamento esperado para uma aplicação Rust. Uma aplicação Rust deve começar a ser executada e só deve encerrar quando todo o código for executado corretamente (ou nunca ser encerrado caso seja um programa de escuta, como um servidor HTTP), mas claro, erros acontecem e para isso que existe o Result.
Bom sem mais explicações. Quando devemos usar unwrap
ou o atalho ?
(sim significa unwrap)? E a resposta é simples!
Quando você tiver absoluta certeza de que o comando não irá causar panico.
Exemplo de uso
Digamos que você está utilizando variáveis de ambiente:
let server_port = std::env::var("SERVER_PORT").unwrap();
Se a variável não tiver sido definida no seu arquivo .env
, isso fará a aplicação entrar em panico. Então será necessário fazer a validação dela:
let server_port = match std::env::var("SERVER_PORT") {
Ok(value) => value,
Err(_) => {
println!("SERVER_PORT não definido, utilizando default");
String::from("3001")
},
};
Mas claro que ter que fazer esse match toda vez que precisar lidar com variáveis do .env é chato demais. Então vou demonstrar como costumo fazer:
env.rs
pub struct Env {
pub server_port: String
}
pub fn init() {
if let Err(error) = std::env::var("SERVER_PORT") {
println!("SERVER_PORT não definido, utilizando default");
// se não ouver um valor default, você pode exibir o erro recuperado pelo let Err e encerrar o processo:
// std::process::exit(1);
};
println!("variáveis validadas com sucesso.")
}
pub fn vars() -> Env {
Env {
server_port: std::env::var("SERVER_PORT").unwrap_or(String::from("3001"))
}
}
A função init
utilizo para validar todas as variáveis fazendo parse de tudo que deve ser convertido. Executo essa função logo no inicio da execução do programa, no arquivo main.rs
:
main.rs
fn main() {
env::init();
// fluxo restante
}
E no restante do código eu utilizo a função vars
que vai recuperar utilizando unwrap (sem validar) as variáveis e entregar diretamente os dados:
server.rs
fn start() {
println!("Servidor iniciado na porta {}", env::vars().server_port);
}
Dessa forma, tenho acesso ràpido as variáveis graças ao unwrap sem risco de ter o programa em panico. Você pode utilizar essa estratégia de validação antes do uso do unwrap
para quase qualquer situação de Result
como conexão com bancos, consumo de APIs externas, fazendo uma verifcação antes e depois disponibilizando uma função que faz o unwrap
para uso direto. Bom isso é tudo pessoal. vlw flw.