Ryana Davisa Ruby QuickRef mówi (bez wyjaśnienia):
Nie ratuj wyjątku. ZAWSZE. albo cię dźgnę.
Dlaczego nie? Co należy zrobić?
Ryana Davisa Ruby QuickRef mówi (bez wyjaśnienia):
Nie ratuj wyjątku. ZAWSZE. albo cię dźgnę.
Dlaczego nie? Co należy zrobić?
Odpowiedzi:
TL; DR : Użyj
StandardError
zamiast tego do wychwytywania wyjątków ogólnych. Kiedy pierwotny wyjątek zostanie ponownie zgłoszony (np. Podczas ratowania w celu zarejestrowania wyjątku), ratowanieException
jest prawdopodobnie w porządku.Exception
jest korzeniem hierarchii wyjątków Ruby , więc gdyrescue Exception
ratowanie od wszystkiego , w tym podklas, takich jakSyntaxError
,LoadError
iInterrupt
.Uratowanie
Interrupt
uniemożliwia użytkownikowi CTRLCzamknięcie programu.Ratowanie
SignalException
zapobiega prawidłowej reakcji programu na sygnały. Będzie to niemożliwe do zabicia, chyba że przezkill -9
.Ratowanie
SyntaxError
oznacza,eval
że porażka zrobi to po cichu.Wszystkie one mogą być wyświetlane przez ten program działa, i stara się CTRLCbądź
kill
to:Ratowanie przed
Exception
nie jest nawet domyślne. Robićnie ratuje
Exception
, ratujeStandardError
. Powinieneś ogólnie określić coś bardziej szczegółowego niż domyślnyStandardError
, ale ratowanie przedException
rozszerzeniem zakresu zamiast zawężeniem go, może mieć katastrofalne skutki i bardzo utrudnić polowanie na błędy.Jeśli masz sytuację, w której chcesz uratować
StandardError
i potrzebujesz zmiennej z wyjątkiem, możesz użyć tego formularza:co jest równoważne z:
Jednym z niewielu typowych przypadków, w których warto ratować,
Exception
jest rejestrowanie / raportowanie, w którym to przypadku należy natychmiast ponownie zgłosić wyjątek:źródło
Throwable
w javaADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
a następnierescue *ADAPTER_ERRORS => e
Prawdziwa zasada: Nie wyrzucać wyjątki. Obiektywność autora cytatu jest wątpliwa, o czym świadczy fakt, że kończy się on
Oczywiście należy pamiętać, że sygnały (domyślnie) generują wyjątki, a zwykle długotrwałe procesy są kończone przez sygnał, więc wychwycenie wyjątku i nie zakończenie wyjątków sygnału sprawi, że program będzie bardzo trudny do zatrzymania. Więc nie rób tego:
Nie, naprawdę, nie rób tego. Nawet nie uruchamiaj tego, aby zobaczyć, czy to działa.
Załóżmy jednak, że masz serwer wątkowy i chcesz, aby wszystkie wyjątki nie:
thread.abort_on_exception = true
).Jest to całkowicie dopuszczalne w wątku obsługi połączenia:
Powyższe działa w odmianie domyślnej procedury obsługi wyjątków Ruby, z tą zaletą, że nie zabija również twojego programu. Rails robi to w module obsługi żądań.
Wyjątki sygnałowe są zgłaszane w głównym wątku. Nici w tle ich nie dostaną, więc nie ma sensu próbować ich tam łapać.
Jest to szczególnie przydatne w środowisku produkcyjnym, w którym nie chcesz, aby Twój program po prostu zatrzymywał się, gdy coś pójdzie nie tak. Następnie możesz wykonać zrzuty stosu w dziennikach i dodać do kodu, aby zająć się określonym wyjątkiem w dalszej części łańcucha połączeń i w bardziej wdzięczny sposób.
Zauważ też, że istnieje inny idiom Ruby, który ma podobny efekt:
W tej linii, jeśli
do_something
podniesie wyjątek, zostaje złapany przez Ruby, wyrzucony ia
przypisany"something else"
.Zasadniczo nie rób tego, z wyjątkiem szczególnych przypadków, w których wiesz , że nie musisz się martwić. Jeden przykład:
The
debugger
funkcja jest raczej dobrym sposobem na ustawienie punktu przerwania w kodzie, ale jeśli działa poza debuggerem i Railsami, zgłasza wyjątek. Teraz teoretycznie nie powinieneś zostawiać kodu debugującego leżącego w twoim programie (pff! Nikt tego nie robi!), Ale możesz chcieć go tam zatrzymać z jakiegoś powodu, ale nie uruchamiać debuggera w sposób ciągły.Uwaga:
Jeśli uruchomiłeś program innej osoby, który przechwytuje wyjątki sygnałów i ignoruje je (powiedz kod powyżej), to:
pgrep ruby
lubps | grep ruby
wyszukaj PID programu, który go narusza, a następnie uruchomkill -9 <PID>
.Jeśli pracujesz z programem innej osoby, który z jakiegoś powodu jest obsadzony tymi blokami wyjątków ignorowania, umieszczenie tego na górze głównej linii jest jednym z możliwych sposobów:
Powoduje to, że program reaguje na normalne sygnały zakończenia, natychmiast kończąc, omijając procedury obsługi wyjątków, bez czyszczenia . Może to spowodować utratę danych lub podobne. Bądź ostrożny!
Jeśli musisz to zrobić:
możesz to zrobić:
W drugim przypadku
critical cleanup
będzie wywoływany za każdym razem, niezależnie od tego, czy zostanie zgłoszony wyjątek.źródło
kill -9
.ensure
będą działać niezależnie od tego, czy wystąpił wyjątek, czy nie, arescue
będą działać tylko wtedy, gdy wyjątek został zgłoszony.TL; DR
Nie rób
rescue Exception => e
wyjątku (i nie podnosz go ponownie) - możesz też zjechać z mostu.Powiedzmy, że jesteś w samochodzie (z Ruby). Niedawno zainstalowałeś nową kierownicę z bezprzewodowym systemem uaktualnień (który wykorzystuje
eval
), ale nie wiedziałeś, że jeden z programistów pomylił się co do składni.Jesteś na moście i zdajesz sobie sprawę, że idziesz trochę w stronę balustrady, więc skręcasz w lewo.
ups! To pewnie Not Good ™, na szczęście Ruby podnosi
SyntaxError
.Samochód powinien natychmiast się zatrzymać - prawda?
Nie.
Można zauważyć, że coś jest nie tak, i zatrzasnąć na przerwach awaryjnych (
^C
:Interrupt
)Tak - to niewiele pomogło. Jesteś dość blisko szyny, więc ustawiasz samochód na parkingu (
kill
ing:)SignalException
.W ostatniej sekundzie wyciągasz klawisze (
kill -9
), a samochód zatrzymuje się, rzucasz się do kierownicy (poduszka powietrzna nie może się napompować, ponieważ nie zatrzymałeś programu z wdziękiem - zakończyłeś go), a komputer z tyłu samochodu wbija się w siedzenie przed nim. Na papier wypełnia się do połowy pełna puszka coli. Artykuły spożywcze z tyłu są kruszone, a większość z nich jest pokryta żółtkiem jaja i mlekiem. Samochód wymaga poważnej naprawy i czyszczenia. (Utrata danych)Mam nadzieję, że masz ubezpieczenie (kopie zapasowe). O tak - ponieważ poduszka powietrzna się nie napompowała, prawdopodobnie jesteś ranny (wyrzucenie z pracy itp.).
Ale poczekaj! Jest
więcejpowody, dla których możesz chcieć użyćrescue Exception => e
!Powiedzmy, że jesteś tym samochodem i chcesz się upewnić, że poduszka powietrzna się napompuje, jeśli samochód przekroczy swój bezpieczny moment zatrzymania.
Oto wyjątek od reguły: Możesz złapać
Exception
tylko wtedy, gdy podniesiesz wyjątek . Lepszą zasadą jest, aby nigdy nie połykaćException
i zawsze podnosić błąd.Ale dodanie ratunku jest łatwe do zapomnienia w języku takim jak Ruby, a umieszczenie oświadczenia ratunkowego tuż przed ponownym podniesieniem problemu wydaje się trochę pozbawione SUSZENIA. I nie chcesz zapomnieć
raise
oświadczenia. A jeśli tak, powodzenia w znalezieniu tego błędu.Na szczęście Ruby jest niesamowity, możesz po prostu użyć
ensure
słowa kluczowego, co zapewnia uruchomienie kodu. Słowoensure
kluczowe uruchomi kod bez względu na wszystko - jeśli zostanie zgłoszony wyjątek, jeśli nie jest, jedynym wyjątkiem jest koniec świata (lub inne mało prawdopodobne zdarzenia).Bum! Ten kod i tak powinien działać. Jedynym powodem, dla którego powinieneś użyć
rescue Exception => e
jest to, że potrzebujesz dostępu do wyjątku lub jeśli chcesz, aby kod działał tylko na wyjątku. I pamiętaj, aby ponownie podnieść błąd. Każdego razu.Uwaga: Jak wskazał @Niall, upewnij się, że zawsze działa. Jest to dobre, ponieważ czasami twój program może cię okłamywać i nie rzucać wyjątków, nawet gdy występują problemy. W przypadku krytycznych zadań, takich jak nadmuchiwanie poduszek powietrznych, musisz upewnić się, że tak się stanie, bez względu na wszystko. Z tego powodu dobrym pomysłem jest sprawdzanie za każdym razem, kiedy samochód się zatrzymuje, czy wyjątek jest zgłaszany. Chociaż nadmuchiwanie poduszek powietrznych jest nieco rzadkim zadaniem w większości kontekstów programowania, w rzeczywistości jest to dość powszechne w przypadku większości zadań czyszczenia.
źródło
ensure
która jest alternatywą dla,rescue Exception
jest myląca - przykład sugeruje, że są one równoważne, ale jak stwierdzono,ensure
stanie się to bez względu na to, czy istnieje wyjątek, czy nie, więc teraz twoje poduszki powietrzne się napompują, ponieważ przekroczyłeś 5 mil na godzinę, chociaż nic nie poszło źle.Ponieważ obejmuje to wszystkie wyjątki. Jest mało prawdopodobne, że Twój program może odzyskać od któregokolwiek z nich.
Powinieneś obsługiwać tylko wyjątki, z których wiesz, jak odzyskać. Jeśli nie spodziewasz się pewnego rodzaju wyjątku, nie obsługuj go, głośno upaść (zapisz szczegóły w dzienniku), a następnie zdiagnozuj dzienniki i napraw kod.
Połknięcie wyjątków jest złe, nie rób tego.
źródło
Jest to szczególny przypadek reguły, że nie należy wychwytywać żadnych wyjątków, z którymi nie można sobie poradzić. Jeśli nie wiesz, jak sobie z tym poradzić, zawsze lepiej jest pozwolić, aby inna część systemu go złapała i obsługiwała.
źródło
Właśnie przeczytałem świetny wpis na blogu na stronie honeybadger.io :
Wyjątek Ruby kontra StandardError: Jaka jest różnica?
źródło