Essa será uma resposta conceitual para o porquê desses avisos existirem, mesmo com abordagens de tentativa com recursos. Infelizmente, também não é o tipo fácil de solução que você espera obter.
A recuperação de erros não pode falhar
finally
modela um fluxo de controle pós-transação que é executado independentemente de uma transação ser bem-sucedida ou falhar.
No caso de falha, finally
captura a lógica que é executada no meio da recuperação de um erro, antes de ser totalmente recuperada (antes de chegarmos ao nosso catch
destino).
Imagine o problema conceitual que ele apresenta para encontrar um erro no meio da recuperação de um erro.
Imagine um servidor de banco de dados em que estamos tentando confirmar uma transação, e ela falha no meio (digamos que o servidor ficou sem memória no meio). Agora, o servidor deseja reverter a transação para um ponto como se nada tivesse acontecido. No entanto, imagine que ele encontre outro erro no processo de reversão. Agora, acabamos tendo uma transação semi-confirmada no banco de dados - a atomicidade e a natureza indivisível da transação estão quebradas e a integridade do banco de dados agora será comprometida.
Esse problema conceitual existe em qualquer linguagem que lide com erros, seja C com propagação manual de código de erro, C ++ com exceções e destruidores ou Java com exceções e finally
.
finally
não pode falhar em linguagens que o fornecem da mesma maneira que os destruidores não podem falhar no C ++ no processo de encontrar exceções.
A única maneira de evitar esse problema conceitual e difícil é garantir que o processo de reverter transações e liberar recursos no meio não possa encontrar uma exceção / erro recursivo.
Portanto, o único design seguro aqui é um design em writer.close()
que não pode falhar. Geralmente, existem maneiras no design de evitar cenários em que essas coisas podem falhar no meio da recuperação, tornando impossível.
Infelizmente, é a única maneira - a recuperação de erros não pode falhar. A maneira mais fácil de garantir isso é tornar esses tipos de funções de "liberação de recursos" e "efeitos colaterais reversos" incapazes de falhar. Não é fácil - a recuperação de erros adequada é difícil e, infelizmente, também é difícil de testar. Mas o caminho para alcançá-lo é garantir que quaisquer funções que "destruam", "fechem", "desliguem", "recuperem" etc. etc. não possam encontrar um erro externo no processo, pois essas funções geralmente precisam ser executadas. ser chamado no meio da recuperação de um erro existente.
Exemplo: Log
Digamos que você queira registrar coisas dentro de um finally
bloco. Isso costuma ser um problema enorme, a menos que o log não possa falhar . O log quase certamente pode falhar, pois pode querer acrescentar mais dados ao arquivo e isso pode facilmente encontrar muitos motivos para falhar.
Portanto, a solução aqui é fazer com que qualquer função de log usada em finally
blocos não seja lançada para o chamador (pode falhar, mas não será lançada). Como podemos fazer isso? Se o seu idioma permitir a execução dentro do contexto de, finalmente, desde que exista um bloco try / catch aninhado, essa seria uma maneira de evitar lançar para o chamador engolindo exceções e transformando-as em códigos de erro, por exemplo, talvez o registro possa ser feito em um separado processo ou encadeamento que pode falhar separadamente e fora de uma pilha de recuperação de erro existente, desenrola-se. Contanto que você possa se comunicar com esse processo sem a possibilidade de encontrar um erro, isso também seria seguro para exceções, pois o problema de segurança só existe nesse cenário se estivermos lançando recursivamente dentro do mesmo encadeamento.
Nesse caso, podemos nos safar da falha de registro, desde que ela não atire, já que deixar de registrar e não fazer nada não é o fim do mundo (não está vazando nenhum recurso ou não revertendo efeitos colaterais, por exemplo).
De qualquer forma, tenho certeza de que você já pode começar a imaginar como é incrivelmente difícil tornar verdadeiramente um software seguro contra exceções. Pode não ser necessário procurar isso ao máximo em todos os softwares menos críticos para a missão. Mas vale a pena tomar nota de como realmente obter segurança de exceção, já que mesmo os autores de bibliotecas de propósito geral geralmente se atrapalham aqui e destroem toda a segurança de exceção de seu aplicativo usando a biblioteca.
SomeFileWriter
Se SomeFileWriter
puder jogar dentro close
, eu diria que geralmente é incompatível com o tratamento de exceções, a menos que você nunca tente fechá-lo em um contexto que envolva a recuperação de uma exceção existente. Se o código estiver fora do seu controle, poderemos ser SOL, mas vale a pena notificar os autores desse flagrante problema de segurança de exceção. Se estiver sob seu controle, minha principal recomendação é garantir que o fechamento não possa falhar por qualquer meio necessário.
Imagine se um sistema operacional realmente falha ao fechar um arquivo. Agora, qualquer programa que tente fechar um arquivo ao desligar falhará . O que devemos fazer agora, basta manter o aplicativo aberto e no limbo (provavelmente não), apenas vazar o recurso do arquivo e ignorar o problema (talvez seja bom se não for tão crítico)? O design mais seguro: torne impossível fechar o arquivo.