Questa sarà una risposta più concettuale al perché questi avvisi potrebbero esistere anche con approcci di prova con le risorse. Sfortunatamente non è anche il tipo semplice di soluzione che potresti sperare di ottenere.
Errore Il recupero non può fallire
finally
modella un flusso di controllo post-transazione che viene eseguito indipendentemente dal fatto che una transazione abbia esito positivo o negativo.
In caso di fallimento, finally
acquisisce la logica che viene eseguita nel mezzo del recupero da un errore, prima che sia stata completamente ripristinata (prima che raggiungiamo la nostra catch
destinazione).
Immagina il problema concettuale che presenta di incontrare un errore nel mezzo del recupero da un errore.
Immagina un server di database in cui stiamo tentando di eseguire il commit di una transazione e non riesce a metà strada (ad esempio il server ha esaurito la memoria nel mezzo). Ora il server vuole ripristinare la transazione fino a un certo punto come se nulla fosse successo. Eppure, immagina di incontrare ancora un altro errore nel processo di rollback. Ora finiamo per avere una transazione parzialmente impegnata nel database: l'atomicità e la natura indivisibile della transazione sono ora interrotte e l'integrità del database sarà ora compromessa.
Questo problema concettuale esiste in qualsiasi linguaggio che tratta degli errori, sia che si tratti di C con propagazione manuale del codice di errore, o C ++ con eccezioni e distruttori, o Java con eccezioni e finally
.
finally
non può fallire nei linguaggi che lo forniscono allo stesso modo in cui i distruttori non possono fallire in C ++ nel processo di incontrare eccezioni.
L'unico modo per evitare questo problema concettuale e difficile è assicurarsi che il processo di rollback delle transazioni e il rilascio di risorse nel mezzo non possa eventualmente incontrare un'eccezione / errore ricorsivo.
Quindi l'unico design sicuro qui è un design in cui writer.close()
non può assolutamente fallire. Di solito ci sono modi nella progettazione per evitare scenari in cui tali cose possono fallire nel mezzo del recupero, rendendolo impossibile.
Purtroppo è l'unico modo: il recupero degli errori non può fallire. Il modo più semplice per garantire ciò è rendere incapaci di fallire quei tipi di "rilascio di risorse" e "effetti collaterali". Non è facile: il corretto recupero degli errori è difficile e purtroppo anche difficile da testare. Ma il modo per ottenerlo è assicurarsi che eventuali funzioni che "distruggono", "chiudi", "arresta", "ripristina", ecc. Non possano eventualmente riscontrare un errore esterno nel processo, poiché tali funzioni dovranno spesso essere chiamato durante il recupero da un errore esistente.
Esempio: registrazione
Supponiamo che tu voglia registrare cose all'interno di un finally
blocco. Questo sarà spesso un grosso problema a meno che la registrazione non possa fallire . La registrazione quasi certamente può fallire, dal momento che potrebbe voler aggiungere più dati al file e che può facilmente trovare molti motivi per fallire.
Quindi la soluzione qui è di renderlo tale che qualsiasi funzione di registrazione utilizzata nei finally
blocchi non possa essere lanciata al chiamante (potrebbe non riuscire, ma non verrà lanciata). Come potremmo farlo? Se la tua lingua consente di lanciare nel contesto di finalmente a condizione che esista un blocco try / catch nidificato, sarebbe un modo per evitare di lanciare il chiamante ingoiando le eccezioni e trasformandole in codici di errore, ad esempio forse la registrazione può essere eseguita in un altro processo o thread che possono fallire separatamente e al di fuori di uno stack di recupero errori esistente svolgersi. Finché puoi comunicare con quel processo senza la possibilità di incorrere in un errore, sarebbe anche sicuro dalle eccezioni, poiché il problema di sicurezza esiste solo in questo scenario se lanciamo ricorsivamente all'interno dello stesso thread.
In questo caso, possiamo farcela con il fallimento della registrazione a condizione che non si getti poiché non riesce a fare il login e non fare nulla non è la fine del mondo (non perde risorse o non riesce a ripristinare gli effetti collaterali, ad esempio).
Ad ogni modo, sono sicuro che puoi già iniziare a immaginare quanto sia incredibilmente difficile rendere un software davvero sicuro. Potrebbe non essere necessario cercarlo al massimo in tutti tranne che nel software più critico. Ma vale la pena prendere nota di come raggiungere veramente la sicurezza delle eccezioni, poiché anche gli autori di librerie per scopi molto generici spesso armeggiano qui e rovinano l'intera sicurezza delle eccezioni dell'applicazione usando la libreria.
SomeFileWriter
Se SomeFileWriter
riesco a lanciarci dentro close
, direi che è generalmente incompatibile con la gestione delle eccezioni, a meno che tu non provi mai a chiuderlo in un contesto che comporta il recupero da un'eccezione esistente. Se il codice è al di fuori del tuo controllo, potremmo essere SOL, ma varrebbe la pena avvisare gli autori di questo palese problema di sicurezza delle eccezioni. Se è sotto il tuo controllo, la mia principale raccomandazione è di assicurarsi che la sua chiusura non possa fallire con qualsiasi mezzo necessario.
Immagina se un sistema operativo potrebbe effettivamente non riuscire a chiudere un file. Ora qualsiasi programma che tenta di chiudere un file all'arresto non si chiuderà . Cosa dovremmo fare ora, basta tenere aperta l'applicazione e nel limbo (probabilmente no), perdere la risorsa del file e ignorare il problema (forse va bene se non è così critico)? Il design più sicuro: rendilo così impossibile non riuscire a chiudere un file.