2015-08-11 30 views
25

Ruby offre due possibilità per causare un'eccezione a livello di codice: raise e fail, entrambi in modalità Kernel. Secondo i documenti, sono assolutamente equivalenti.Fail vs raise in Ruby: dovremmo davvero credere alla guida allo stile?

Per abitudine, ho utilizzato solo raise finora. Ora ho trovato diversi consigli (ad esempio here), per utilizzare raise per le eccezioni da rilevare e fail per errori gravi che non devono essere gestiti.

Ma ha davvero senso? Quando scrivi una classe o un modulo e causi un problema in profondità, che segnali tramite fail, i tuoi colleghi di programmazione che stanno rivedendo il codice potrebbero felicemente capire le tue intenzioni, ma la persona che è utilizzando il codice sarà molto probabilmente non guardare il mio codice e non ha modo di sapere se l'eccezione è stata causata da un raise o da fail. Quindi, il mio uso attento di raise o fail non può avere alcuna influenza sulla sua decisione, se dovrebbe o non dovrebbe gestirla.

Qualcuno potrebbe vedere difetti nei miei argomenti? O ci sono altri criteri, che potrei voler usare fail invece di raise?

+3

Penso che questa domanda possa essere considerata off-topic, in quanto non è un problema specifico, quindi secondo me è più adatta ad esempio a [Programmers] (http://programmers.stackexchange.com/ –

+2

@ m.cekiera quando si riferiscono ad altri siti, è spesso utile indicare che [il cross-posting è disapprovato] (http://meta.stackexchange.com/tags/cross-posting/info) – gnat

+0

@ m.cekiera : In realtà, il problema è sorto da un problema specifico (sto scrivendo una libreria e voglio sollevare un'eccezione, quando un argomento passato a un metodo è fuori portata), ma ho pensato che fornire questa informazione non aiuta a rispondere alla domanda. Grazie comunque per il commento. – user1934428

risposta

34

uso 'rilancio' per le eccezioni di essere catturati, e 'fallire' per gli errori gravi che non sono destinate ad essere gestiti

Questo non è ciò che il official style guide o il link che hai fornito dicono sul importa.

Ciò che si intende qui è utilizzare raise solo nei blocchi rescue. Aka usa fail quando vuoi dire qualcosa è in mancanza e usa raise quando rilanciare un'eccezione.

Per quanto riguarda lo "importa" parte - non è una delle regole più rigorosamente seguite rigorosamente, ma si potrebbe fare lo stesso argomento per qualsiasi convenzione. Si dovrebbe seguire in questo ordine:

  1. Il tuo stile di guida del progetto
  2. La vostra guida di stile aziendale
  3. La comunità guida di stile

Idealmente, i tre dovrebbe essere lo stesso.


Aggiornamento: Come di this PR (Dicembre 2015), la convenzione è di usare sempre raise.

+2

Grazie per aver chiarito questo! Questo mi rende chiaro (anche se sono ancora un po 'perplesso sull'utilità di questa regola, perché in generale posso facilmente vedere dal codice, se sto rilanciando un'eccezione o meno. Per quanto ho capito, non si guadagna nulla enfatizzando ulteriormente questo fatto utilizzando un comando diverso: – user1934428

+1

@ user1934428, è solo il nome giusto per il caso corretto.Inoltre, immagina di voler eseguire un po 'più di elaborazione che avvolge l'eccezione originale (ad esempio) in caso di un Pertanto, potresti creare un metodo privato dedicato per questo, sarà utile vedere 'raise' invece di' fail' lì perché il rethrowing non sarà preceduto da 'rescue'. – ndn

+0

Buon punto! Accetto ... . – user1934428

1

una volta ho avuto una conversazione con Jim Weirich di questa cosa molto, allora ho sempre usato fail quando il mio metodo è esplicitamente fallendo per qualche motivo e raise eccezioni di ri-gettato.

Ecco un post con un messaggio da Jim (quasi alla lettera a quello che ha detto a me in persona): http://www.virtuouscode.com/2014/05/21/jim-weirich-on-exceptions/

Ecco il testo pertinente dal post, una citazione attribuita a Jim:

Ecco la mia filosofia di base (e altri pensieri casuali) sulle eccezioni.

Quando si chiama un metodo, si hanno certe aspettative su ciò che il metodo compirà. Formalmente, queste aspettative sono chiamate post-condizioni. Un metodo dovrebbe generare un'eccezione ogni volta che non riesce a soddisfare le sue postcondizioni.

Per utilizzare efficacemente questa strategia, è necessario disporre di una conoscenza limitata di Design by Contract e del significato delle pre e post-condizioni. Penso che sia una buona cosa sapere comunque.

Ecco alcuni esempi concreti. Il modello save metodo di Rails:

model.save! 
-- post-condition: The model object is saved. 

Se il modello non viene salvato per qualche motivo, quindi un'eccezione deve essere sollevato perché il post-condizione non è soddisfatta.

model.save 
-- post-condition: (the model is saved && result == true) || 
        (the model is not saved && result == false) 

Se save in realtà non salva, poi il risultato restituito sarà false, ma il post-condizione è ancora soddisfatta, quindi non fa eccezione.

Trovo interessante il fatto che il metodo save! abbia una post-condizione molto più semplice.

Per quanto riguarda il salvataggio delle eccezioni, ritengo che un'applicazione debba disporre di punti strategici in cui vengono salvate le eccezioni. C'è poca necessità di salvataggio/rilancio per la maggior parte. L'unica volta in cui vorresti salvare e rilanciare è quando hai un lavoro a metà strada e vuoi annullare qualcosa per evitare uno stato parzialmente completo. I punti strategici di salvataggio dovrebbero essere scelti con cura in modo che il programma possa continuare con altri lavori, anche se l'operazione corrente non ha funzionato. I programmi di elaborazione delle transazioni dovrebbero semplicemente passare alla transazione successiva. Un'app Rails dovrebbe essere ripristinata ed essere pronta a gestire la prossima richiesta http.

La maggior parte dei gestori di eccezioni deve essere generica. Poiché le eccezioni indicano un errore di qualche tipo, il gestore deve solo prendere una decisione su cosa fare in caso di errore. Le operazioni di recupero dettagliate per eccezioni molto specifiche sono generalmente scoraggiate a meno che il gestore non sia molto vicino (call graph wise) al punto dell'eccezione.

Le eccezioni non devono essere utilizzate per il controllo di flusso, utilizzare throw/catch per quello. Questo riserva eccezioni per le vere condizioni di fallimento.

(Una parentesi, perché io uso eccezioni per indicare fallimenti, ho quasi sempre utilizzare la parola chiave fail piuttosto che la parola chiave raise in Ruby. Fail e raise sono sinonimi in modo non v'è alcuna differenza se non che fail communcates più chiaramente che il metodo L'unica volta che uso lo raise è quando sto rilevando un'eccezione e la rilancio, perché qui sono non in errore, ma in modo esplicito e mirato a sollevare un'eccezione. Questo è un problema stilistico che seguo, ma io dubbio molte altre persone lo fanno).

Ecco qui, una memoria piuttosto sconclusionata sui miei pensieri sulle eccezioni.

So che ci sono molte guide di stile che non sono d'accordo (il style guide utilizzato da RoboCop, per esempio). Non mi interessa Jim mi ha convinto.