2017-01-07 41 views
8

Zdefiniuję wartość. Ale ta wartość może mieć wartość klucza hasza. Będę używał ratunkowej dla zdefiniowania wartości jest zerowej, jeśli te klucze nie istnieją. npDlaczego powinniśmy unikać używania ratunku w jego formie modyfikatora?

foo = bar[:a][:b][:c] rescue nil

Jednak w praktyce mi zły styl, bo ja za pomocą na ratowanie w formie modyfikatora. Zmienię logikę, aby użyć warunku sprawdzenia trzech.

foo = bar[:a][:b][:c] if bar.key?(:a) && bar[:a].key?(:b) && bar[:a][:b].key?(:c)

Naprawdę chciałbym wiedzieć, dlaczego powinniśmy unikać ratunku w postaci modyfikatora w szynach?

+1

Warto zauważyć, że choć jest to zły styl, istnieją dla niego uzasadnione zastosowania. –

Odpowiedz

14

Dlaczego powinniśmy unikać ratowania w formie modyfikatora w szynach?

Po pierwsze, dlatego, że ukrywa wszystkie błędów, w tym te, które Ci się spodziewać i te, które Ci nie, i koc rescue nie uświadomić przyszłych czytelników kodzie które były błędy oczekiwane lub nieoczekiwane. To może nie być problemem teraz, z prostym foo[:a][:b][:c], ale w danym momencie czasowym ktoś może modyfikować że oświadczenie czytać foo[:a][:b][some_method] i nagle jakieś błędy, które powinny bańka z some_method są również połknięciu.

Po drugie, zwykle istnieje lepsze, mniej kompleksowe rozwiązanie, które jest bardziej jawnie zaprojektowane, aby obsłużyć tylko błąd, który zamierzamy zignorować: brakujący indeks lub wartość zwracana nil.

W twoim przypadku alternatywą jest , a nie ogromny sugerowany przez Ciebie if && && &&. Dla hash, można użyć dig, który posiada wszystkie zalety rescue bez połykania każdy typ wyjątku, który mógłby zostać podniesiony:

foo = bar.dig(:a, :b, :c) 

Podobnie, dla metody W powiązanych inwokacji, można użyć try (w Rails) lub bezpiecznego operator nawigacji (w Ruby 2.3):

foo = bar.try(:a).try(:b).try(:c) 
# or 
foo = bar&.a&.b&.c 
+0

IMHO warto zauważyć, że podniesienie wyjątku jest dość kosztowną operacją. Zwrócenie 'nil' za pomocą jednej z alternatyw jest zwykle szybsze w przypadkach, w których klucz nie istnieje. – spickermann

0

typowym przykładem, dlaczego jest to zły pomysł:

foo = ban[:a][:b][:c] rescue nil 

Możesz stracić dużo czasu sprawdzając, czy bar ma naprawdę {a: {b: {c: :something}}} i zastanawiasz się, dlaczego foo to nil: istnieje literówka, a rescue nil ukrywa to.

Aby uzyskać alternatywy, zobacz dobrą odpowiedź @ meagar.

1

Unikać

... rescue ... 

z tego samego powodu jak

begin 
    ... 
rescue 
    ... 
end 

Ani określa klasę wyjątku, a więc są cicho pomijając wszystkie błędy, a nie tylko jednego, że można się spodziewać. Powinieneś zawsze być tak dokładny, jak to tylko możliwe, kiedy ratujesz błędy.

Występuje również duży koszt tworzenia wyjątku.

Po podniesieniu wyjątku śledzenie jest wypełniane, a w Railsach tworzenie od 500 do 1000 ciągów z nazwami plików i numerami wierszy, ponieważ Rails ma zwykle głęboki stos wywołań. Więc jeśli umieścisz swoją pętlę w pętli, może to z łatwością doprowadzić do utworzenia tysięcy obiektów łańcuchowych, które nigdy nie będą używane, a przy nieczytelnym zbieraniu śmieci Ruby będzie to miało wpływ na wydajność.

Stąd, jeśli to możliwe spróbuj użyć alternatywnych, które nie budzą wyjątek

foo = bar.dig(:a, :b, :c) 
3

Dłuższa forma jest bardziej bezpieczny, nie chciałbym używać skróconej wersji produkcji, chyba że nie ma innego sposobu. To, czy korzystasz z czeków, czy z wyjątkiem, aby uniknąć błędów lub złapać, zależy od sytuacji. Wyjątki są kosztowne w czasie pracy procesora, więc twoje testy porównawcze dla czasochłonnych metod, z drugiej strony robienie wielu czeków i wciąż nie pewności jest być może gorszym. Jeśli czytelność mojego kodu zostałaby utracona, wykonując wiele kontroli i szybkość nie jest czynnikiem, używam rozpocząć ratowanie lub def .. ratowanie, ale w takim przypadku lepiej uratować znany wyjątek, taki jak ten

begin 
    # - 
    raise "another exception" 
rescue SyntaxError 
    # - 
rescue => exception 
    @errors += 1 
    log exception 
    log exception.backtrace 
end 

który daje

another exception 
C:/.../test.rb:3:in `<main>' 

Zawsze złapać rodzaj wyjątku i umieścić albo lepiej zalogować się, ja też go użyć do podniesienia zmiennej @errors które jest rejestrowane dla wszystkich moich skryptów i monitorowane przez odrębny narzędzie.