Às vezes, coisas ruins acontecem em seu código e não há nada que você possa fazer a respeito. Nesses casos, Rust tem a macro panic!
. Quando a macro panic!
for executada, seu programa imprimirá uma mensagem de falha, desenrolará e limpará a pilha e, em seguida, encerrará. Isso ocorre mais comumente quando um bug de algum tipo foi detectado e não está claro para o programador como lidar com o erro.
Desenrolando a pilha ou abortando em resposta a um pânico
Por padrão, quando ocorre um pânico, o programa começa a se desenrolar, o que significa que Rust volta a subir a pilha e limpa os dados de cada função que encontra. Mas essa caminhada de volta e limpeza dá muito trabalho. A alternativa é abortar imediatamente, o que termina o programa sem limpar. A memória que o programa estava usando precisará ser limpa pelo sistema operacional. Se em seu projeto você precisar tornar o binário resultante o menor possível, você pode mudar de desenrolamento para abortar em caso de pânico adicionando
panic = 'abort'
nas seções[profile]
apropriadas em seu arquivo Cargo.toml. Por exemplo, se você deseja abortar em pânico no modo de liberação, adicione isto:
[profile.release] panic = 'abort'
Vamos tentar chamar panic!
num programa simples:
Nome do arquivo: src/main.rs
fn main() {
panic!("crash and burn");
}
Ao executar o programa, você verá algo assim:
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/panic`
thread 'main' panicked at 'crash and burn', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
A chamada para panic!
causa a mensagem de erro contida nas duas últimas linhas. A primeira linha mostra nossa mensagem de pânico e o local em nosso código-fonte onde o pânico ocorreu: src/main.rs: 2: 5 indica que é a segunda linha, quinto caractere de nosso arquivo src/main.rs.
Neste caso, a linha indicada faz parte do nosso código, e se formos para essa linha, veremos a chamada da macro panic!
. Em outros casos, a chamada panic!
pode estar no código que nosso código chama, e o nome do arquivo e o número da linha relatados pela mensagem de erro serão o código de outra pessoa onde a macro panic!
é chamada, não a linha do nosso código que eventualmente levou à chamada panic!
. Podemos usar o backtrace das funções de onde panic!
foi chamado para descobrir a parte do nosso código que está causando o problema. Discutiremos o que é um backtrace com mais detalhes a seguir.
Usando um Backtrace panic!
Vejamos outro exemplo para ver como é quando uma chamada panic!
vem de uma biblioteca por causa de um bug em nosso código, em vez de nosso código chamando a macro diretamente. A Listagem 9-1 contém alguns códigos que tentam acessar um elemento por índice em um vetor.
Nome do arquivo: src/main.rs
Este código entra em pânico!
fn main() {
let v = vec![1, 2, 3];
v[99];
}
Aqui, estamos tentando acessar o 100º elemento de nosso vetor (que está no índice 99 porque a indexação começa em zero), mas ele tem apenas 3 elementos. Nesta situação, Rust entrará em pânico. O uso de []
deve retornar um elemento, mas se você passar um índice inválido, não há nenhum elemento que Rust pudesse retornar aqui que seria correto.
Em C, tentar ler além do final de uma estrutura de dados é um comportamento indefinido. Você pode obter o que quer que esteja no local da memória que corresponda a esse elemento na estrutura de dados, mesmo que a memória não pertença a essa estrutura. Isso é chamado de buffer overread e pode levar a vulnerabilidades de segurança se um invasor for capaz de manipular o índice de forma a ler dados que não deveriam ter permissão para serem armazenados após a estrutura de dados.
Para proteger seu programa desse tipo de vulnerabilidade, se você tentar ler um elemento em um índice que não existe, o Rust interromperá a execução e se recusará a continuar. Vamos experimentar e ver:
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/panic`
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Isso aponta erro em um arquivo que não escrever, libcore/fatia/mod.rs. Essa é a implementação de slice
no código-fonte do Rust. O código que é executado quando usamos []
em nosso vetor v
está em libcore/slice/mod.rs, e é onde o panic!
está realmente acontecendo.
A próxima linha de observação nos diz que podemos definir a variável RUST_BACKTRACE
de ambiente para obter um backtrace de exatamente o que aconteceu para causar o erro. Um backtrace é uma lista de todas as funções que foram chamadas para chegar a este ponto. Os backtraces no Rust funcionam como em outras línguas: a chave para ler o backtrace é começar do início e ler até ver os arquivos que você escreveu. Esse é o local onde o problema se originou. As linhas acima das linhas que mencionam seus arquivos são códigos que seu código chamou; as linhas abaixo são códigos que chamaram seu código. Essas linhas podem incluir código Rust central, código de biblioteca padrão ou crates que você está usando. Vamos tentar obter um backtrace definindo a variável RUST_BACKTRACE
de ambiente para qualquer valor, exceto 0. A Listagem 9-2 mostra uma saída semelhante ao que você verá.
$ RUST_BACKTRACE=1 cargo run
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5
stack backtrace:
0: rust_begin_unwind
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/std/src/panicking.rs:483
1: core::panicking::panic_fmt
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/panicking.rs:85
2: core::panicking::panic_bounds_check
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/panicking.rs:62
3: <usize as core::slice::index::SliceIndex<[T]>>::index
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/slice/index.rs:255
4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/slice/index.rs:15
5: <alloc::vec::Vec<T> as core::ops::index::Index<I>>::index
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/alloc/src/vec.rs:1982
6: panic::main
at ./src/main.rs:4
7: core::ops::function::FnOnce::call_once
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/ops/function.rs:227
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
É muita produção! A saída exata que você vê pode ser diferente dependendo do seu sistema operacional e da versão do Rust. Para obter backtraces com essas informações, os símbolos de depuração devem ser habilitados. Os símbolos de depuração são ativados por padrão ao usar cargo build
ou cargo run
sem o sinalizador --release
, como temos aqui.
Na saída na Listagem 9-2, linha 6 dos backtrace aponta para a linha em nosso projeto que está causando o problema: linha 4 do arquivo src/main.rs. Se não quisermos que nosso programa entre em pânico, o local apontado pela primeira linha que menciona um arquivo que escrevemos é onde devemos começar a investigar. Na Listagem 9-1, onde escrevemos deliberadamente o código que entraria em pânico para demonstrar como usar backtraces, a maneira de consertar o pânico é não solicitar um elemento no índice 99 de um vetor que contém apenas 3 itens. Quando seu código entrar em pânico no futuro, você precisará descobrir que ação o código está realizando com quais valores causam o pânico e o que o código deve fazer em vez disso.
Voltaremos para panic!
e quando devemos e não devemos usar panic!
para lidar com as condições de erro na seção “Chamar panic!
ou Não chamar panic!
” posteriormente neste capítulo. A seguir, veremos como se recuperar de um erro usando Result
.
Traduzido por Acervo Lima. O original pode ser acessado aqui.
0 comentários:
Postar um comentário