Do Go
twórcy języka napisać :
Go nie zapewnia asercji. Są niezaprzeczalnie wygodne, ale z naszego doświadczenia wynika, że programiści używają ich jako narzędzia, aby uniknąć myślenia o właściwej obsłudze błędów i zgłaszaniu błędów. Właściwa obsługa błędów oznacza, że serwery kontynuują działanie po błędach niekrytycznych zamiast awarii. Właściwe raportowanie błędów oznacza, że błędy są bezpośrednie i do rzeczy, oszczędzając programiście interpretacji dużego śladu awarii. Precyzyjne błędy są szczególnie ważne, gdy programista widzący błędy nie zna kodu.
Jaka jest twoja opinia na ten temat?
reflect.DeepEqual
, na pewno nie potrzebujesz go. Jest to wygodne, ale kosztem wydajności (testy jednostkowe są dobrym przykładem użycia). W przeciwnym razie możesz bez problemu wprowadzić kontrolę równości odpowiednią dla Twojej „kolekcji”.for
pętli w Go (podobnie jak C). To byłoby naprawdę miło mieć ogólne operacje plaster, chociaż porównanie komplikuje, gdy wskaźniki i kodowanym są zaangażowane.Odpowiedzi:
Nie, nie ma w tym nic złego, o
assert
ile używasz go zgodnie z przeznaczeniem.Oznacza to, że ma służyć do wychwytywania przypadków, które „nie mogą się zdarzyć” podczas debugowania, w przeciwieństwie do normalnej obsługi błędów.
źródło
Nie, ani
goto
nieassert
są złe. Ale oba mogą być niewłaściwie wykorzystywane.Assert służy do kontroli poczytalności. Rzeczy, które powinny zabić program, jeśli są niepoprawne. Nie do sprawdzania poprawności lub jako zamiennik obsługi błędów.
źródło
goto
mądrze korzystać ?goto
ze względów czysto religijnych, a następnie po prostu używajgoto
zamiast zaciemniać to, co robisz. Innymi słowy: jeśli możesz udowodnić, że naprawdę potrzebujeszgoto
, a jedyną alternatywą jest wprowadzenie mnóstwa bezcelowego rusztowania, które ostatecznie robi to samo, ale bez zmiany policji Goto ... to po prostu użyjgoto
. Oczywiście warunkiem tego jest „jeśli możesz udowodnić, że naprawdę potrzebujeszgoto
”. Często ludzie nie. To wciąż nie oznacza, że z natury jest to Zła Rzecz.goto
jest używany w jądrze Linuksa do czyszczenia koduZgodnie z tą logiką punkty przerwania również są złe.
Aserty powinny być używane jako pomoc w debugowaniu i niczym więcej. „Zło” występuje, gdy próbujesz ich użyć zamiast obsługi błędów.
Zapewnienia służą pomocą, programistom, w wykrywaniu i naprawianiu problemów, które nie mogą istnieć, oraz weryfikacji, czy twoje założenia pozostają prawdziwe.
Nie mają one nic wspólnego z obsługą błędów, ale niestety niektórzy programiści nadużywają ich jako takich, a następnie ogłaszają, że są „źli”.
źródło
Lubię używać aser. Uważam, że jest to bardzo przydatne, gdy buduję aplikacje po raz pierwszy (być może dla nowej domeny). Zamiast bardzo fantazyjnego sprawdzania błędów (które rozważałbym przedwczesną optymalizację) koduję szybko i dodam wiele stwierdzeń. Po tym, jak dowiem się więcej o tym, jak działają, robię przepisywanie i usuwam niektóre z twierdzeń i zmieniam je, aby poprawić obsługę błędów.
Z powodu stwierdzeń spędzam dużo mniej czasu na programowaniu / debugowaniu programów.
Zauważyłem również, że twierdzenia pomagają mi myśleć o wielu rzeczach, które mogłyby uszkodzić moje programy.
źródło
Jako dodatkową informację go udostępnia wbudowaną funkcję
panic
. Można tego użyć zamiastassert
. Na przykładpanic
wypisze ślad stosu, więc w jakiś sposób ma to celassert
.źródło
Powinny być używane do wykrywania błędów w programie. Niezły wkład użytkownika.
Jeżeli są stosowane prawidłowo, są nie złe.
źródło
To często się pojawia i myślę, że jednym z problemów, który sprawia, że obrona twierdzeń jest myląca, jest to, że często opierają się one na sprawdzaniu argumentów. Rozważ ten inny przykład, kiedy możesz użyć twierdzenia:
Używasz wyjątku dla danych wejściowych, ponieważ spodziewasz się, że czasami otrzymasz złe dane wejściowe. Twierdzisz, że lista jest posortowana, aby pomóc ci znaleźć błąd w algorytmie, którego z definicji nie oczekujesz. Asercja jest tylko w kompilacji debugowania, więc nawet jeśli sprawdzenie jest drogie, nie masz nic przeciwko robieniu tego przy każdym wywołaniu procedury.
Nadal musisz przetestować kod produkcyjny jednostkowo, ale jest to inny i uzupełniający sposób upewnienia się, że kod jest poprawny. Testy jednostkowe upewniają się, że rutyna spełnia wymagania interfejsu, a twierdzenia są bardziej szczegółowym sposobem upewnienia się, że implementacja działa dokładnie tak, jak tego oczekujesz.
źródło
Twierdzenia nie są złe, ale można je łatwo nadużyć. Zgadzam się ze stwierdzeniem, że „twierdzenia są często wykorzystywane jako narzędzie, które pozwala uniknąć myślenia o właściwej obsłudze błędów i zgłaszaniu błędów”. Widziałem to dość często.
Osobiście lubię korzystać z asercji, ponieważ dokumentują one założenia, które mogłem poczynić podczas pisania mojego kodu. Jeśli te założenia zostaną złamane przy zachowaniu kodu, problem można wykryć podczas testu. Jednak staram się usuwać wszystkie twierdzenia z mojego kodu podczas tworzenia kompilacji produkcyjnej (tj. Przy użyciu #ifdefs). Usuwając twierdzenia z kompilacji produkcyjnej, eliminuję ryzyko, że ktoś nadużyje ich jako kuli.
Istnieje również inny problem z twierdzeniami. Asercje są sprawdzane tylko w czasie wykonywania. Ale często zdarza się, że sprawdzenie, które chcesz wykonać, mogło zostać wykonane w czasie kompilacji. Lepiej jest wykryć problem w czasie kompilacji. Dla programistów C ++ boost zapewnia BOOST_STATIC_ASSERT, który pozwala to zrobić. Dla programistów C ten artykuł ( tekst linku ) opisuje technikę, której można użyć do wykonywania asercji w czasie kompilacji.
Podsumowując, kieruję się następującą zasadą: Nie używaj asercji w kompilacji produkcyjnej i, jeśli to możliwe, używaj tylko asercji dla rzeczy, których nie można zweryfikować w czasie kompilacji (tj. Muszą być sprawdzone w czasie wykonywania).
źródło
Przyznaję, że użyłem twierdzeń, nie rozważając jednak właściwego zgłaszania błędów. Nie oznacza to jednak, że są one bardzo pomocne, gdy są właściwie używane.
Są one szczególnie przydatne, jeśli chcesz przestrzegać zasady „Crash Early”. Załóżmy na przykład, że wdrażasz mechanizm zliczania referencji. W niektórych lokalizacjach w kodzie wiesz, że przelicznik powinien wynosić zero lub jeden. Przypuśćmy również, że jeśli ponowne zliczanie jest błędne, program nie zawiedzie natychmiast, ale w następnej pętli komunikatów trudno będzie ustalić, dlaczego coś poszło nie tak. Potwierdzenie byłoby pomocne w wykryciu błędu bliższego jego początkowi.
źródło
Wolę unikać kodu, który robi różne rzeczy podczas debugowania i wydawania.
Jednak przydatne jest włamanie się do debuggera i posiadanie wszystkich informacji o pliku / linii, a także dokładne wyrażenie i dokładną wartość.
Posiadanie twierdzenia, które „ocenia stan tylko podczas debugowania” może być optymalizacją wydajności i jako takie jest przydatne tylko w 0,0001% programów - gdzie ludzie wiedzą, co robią. We wszystkich innych przypadkach jest to szkodliwe, ponieważ wyrażenie może faktycznie zmienić stan programu:
assert(2 == ShroedingersCat.GetNumEars());
sprawi, że program będzie robił różne rzeczy podczas debugowania i wydawania.Opracowaliśmy zestaw makr asercji, które rzucałyby wyjątek i robiły to zarówno w wersji debugowania, jak i wersji. Na przykład
THROW_UNLESS_EQ(a, 20);
wyrzuciłby wyjątek z komunikatem what () zawierającym zarówno plik, linię, jak i rzeczywiste wartości a itd. Tylko makro miałoby na to moc. Debuger może być skonfigurowany tak, aby łamał się podczas „rzucania” określonego typu wyjątku.źródło
Nie lubię twierdzeń intensywnie. Nie posunąłbym się nawet do stwierdzenia, że są źli.
Zasadniczo, assert zrobi to samo co niesprawdzony wyjątek, jedynym wyjątkiem jest to, że assert (normalnie) nie powinien być zachowany dla produktu końcowego.
Jeśli budujesz sieć bezpieczeństwa dla siebie podczas debugowania i budowania systemu, dlaczego odmawiasz tej siatki bezpieczeństwa swojemu klientowi, działowi pomocy technicznej lub komukolwiek, kto będzie mógł korzystać z oprogramowania, które obecnie budujesz. Używaj wyjątków wyłącznie w odniesieniu do stwierdzeń i wyjątkowych sytuacji. Tworząc odpowiednią hierarchię wyjątków, będziesz w stanie bardzo szybko rozróżnić jeden od drugiego. Z wyjątkiem tego czasu twierdzenie pozostaje na miejscu i może dostarczyć cennych informacji w przypadku awarii, która w przeciwnym razie zostałaby utracona.
Tak więc w pełni rozumiem twórców Go, usuwając całkowicie twierdzenia i zmuszając programistów do korzystania z wyjątków do obsługi sytuacji. Istnieje proste wytłumaczenie tego, wyjątek jest po prostu lepszym mechanizmem dla pracy, dlaczego trzymać się archaicznych twierdzeń?
źródło
Krótka odpowiedź: Nie, uważam, że twierdzenia mogą być przydatne
źródło
Niedawno zacząłem dodawać niektóre elementy do mojego kodu i tak to zrobiłem:
Mentalnie dzielę swój kod na kod graniczny i kod wewnętrzny. Kod graniczny to kod, który obsługuje dane wejściowe użytkownika, odczytuje pliki i pobiera dane z sieci. W tym kodzie żądam danych wejściowych w pętli, która wychodzi tylko wtedy, gdy dane wejściowe są prawidłowe (w przypadku interaktywnego wprowadzania danych przez użytkownika), lub zgłaszam wyjątki w przypadku nieodwracalnych uszkodzonych danych pliku / sieci.
Kod wewnętrzny to wszystko inne. Na przykład funkcję, która ustawia zmienną w mojej klasie, można zdefiniować jako
a funkcja otrzymująca dane wejściowe z sieci może odczytywać jako taka:
To daje mi dwie warstwy czeków. Wszystko, gdzie dane są określane w czasie wykonywania, zawsze otrzymuje wyjątek lub natychmiastową obsługę błędów. Jednak to dodatkowe sprawdzenie
Class::f
za pomocąassert
instrukcji oznacza, że jeśli jakiś wewnętrzny kod kiedykolwiek zadzwoniClass::f
, nadal będę miał kontrolę poprawności. Mój wewnętrzny kod może nie przekazać poprawnego argumentu (ponieważ mogłem obliczyćvalue
z pewnej złożonej serii funkcji), więc lubię mieć asercję w funkcji ustawiającej, aby udokumentować, że niezależnie od tego, kto wywołuje funkcję,value
nie może być większa niż lub równyend
.Wydaje się, że pasuje to do tego, co czytam w kilku miejscach, że stwierdzenia powinny być niemożliwe do pogwałcenia w dobrze funkcjonującym programie, podczas gdy wyjątki powinny dotyczyć wyjątkowych i błędnych przypadków, które są nadal możliwe. Ponieważ teoretycznie sprawdzam poprawność wszystkich danych wejściowych, nie powinno być możliwe wyzwolenie mojego twierdzenia. Jeśli tak, mój program jest nieprawidłowy.
źródło
Jeśli twierdzenia, o których mówisz, oznaczają, że program wymiotuje, a następnie istnieje, twierdzenia mogą być bardzo złe. Nie oznacza to, że zawsze są niewłaściwe w użyciu, są konstrukcją, którą bardzo łatwo można niewłaściwie wykorzystać. Mają też wiele lepszych alternatyw. Takie rzeczy są dobrymi kandydatami do bycia nazywanymi złymi.
Na przykład moduł innej firmy (lub jakikolwiek inny moduł) prawie nigdy nie powinien wychodzić z programu wywołującego. Nie daje to programiście wywołującemu żadnej kontroli nad ryzykiem, jakie program powinien podjąć w tym momencie. W wielu przypadkach dane są tak ważne, że nawet zapisanie uszkodzonych danych jest lepsze niż ich utrata. Aserty mogą zmusić Cię do utraty danych.
Niektóre alternatywy dla stwierdzeń:
Niektóre referencje:
Nawet osoby, które opowiadają się za twierdzeniem, uważają, że powinny być one wykorzystywane wyłącznie w rozwoju, a nie w produkcji:
Ta osoba twierdzi, że twierdzeń należy używać, gdy moduł ma potencjalnie uszkodzone dane, które nadal występują po zgłoszeniu wyjątku: http://www.advogato.org/article/949.html . Jest to z pewnością uzasadniony punkt, jednak moduł zewnętrzny nigdy nie powinien określać, jak ważne są uszkodzone dane dla programu wywołującego (wychodząc z nich „za”). Właściwym sposobem na poradzenie sobie z tym jest zgłoszenie wyjątku, który wyjaśnia, że program może być teraz w niespójnym stanie. A ponieważ dobre programy składają się głównie z modułów (z małym kodem kleju w głównym pliku wykonywalnym), twierdzenia są prawie zawsze niewłaściwą rzeczą do zrobienia.
źródło
assert
jest bardzo przydatny i pozwala zaoszczędzić wiele czasu na cofanie się, gdy wystąpią nieoczekiwane błędy, zatrzymując program przy pierwszych oznakach problemów.Z drugiej strony bardzo łatwo jest nadużyć
assert
.Właściwa, poprawna wersja wyglądałaby mniej więcej tak:
Więc ... na dłuższą metę ... na dużym obrazie ... Muszę się zgodzić, że
assert
można to wykorzystać. Ciągle to robię.źródło
assert
jest nadużywany do obsługi błędów, ponieważ mniej pisze.Dlatego jako projektanci języków powinni raczej zauważyć, że poprawną obsługę błędów można wykonać nawet przy mniejszym pisaniu. Wykluczenie asercji, ponieważ mechanizm wyjątków jest pełny, nie jest rozwiązaniem. Och, czekaj, Go też nie ma wyjątków. Szkoda :)
źródło
Kiedy to zobaczyłem, miałem ochotę kopnąć autora w głowę.
Cały czas używam twierdzeń w kodzie i ostatecznie je wszystkie zastępuję, gdy piszę więcej kodu. Używam ich, gdy nie napisałem wymaganej logiki i chcę otrzymywać powiadomienia, gdy napotkam kod, zamiast pisać wyjątek, który zostanie usunięty, gdy projekt będzie się kończył.
Wyjątki łatwiej wtapiają się w kod produkcyjny, co mi się nie podoba. Twierdzenie jest łatwiejsze do zauważenia niż
throw new Exception("Some generic msg or 'pretend i am an assert'");
źródło
Mój problem z tymi odpowiedziami broniącymi twierdzenia nie polega na tym, że nikt wyraźnie nie odróżnia go od zwykłego błędu krytycznego i dlaczego twierdzenie nie może być podzbiorem wyjątku . Co powiesz na to, a jeśli wyjątek nigdy nie zostanie złapany? Czy to sprawia, że jest to stwierdzenie nomenklatury? I dlaczego miałbyś kiedykolwiek chcieć narzucić ograniczenie w języku, w którym można zgłaszać wyjątek, z którym / nic / nie można sobie poradzić?
źródło
func()
z liczbą ujemną. Teraz, nagle z twoimi zapewnieniami, wyciągnąłeś dywan spod mnie i nie dałeś mi szansy na wyzdrowienie, zamiast grzecznie powiedzieć mi, czego żądam, nie można zrobić. Nie ma nic złego w oskarżaniu programu o niewłaściwe zachowanie i cytowaniu go: problem polega na tym, że zacierasz akt egzekwowania prawa i skazujesz przestępcę.Tak, twierdzenia są złe.
Często przyzwyczajają się w miejscach, w których należy zastosować właściwą obsługę błędów. Przyzwyczaj się do pisania poprawnej obsługi błędów jakości produkcji od samego początku!
Zwykle przeszkadzają w pisaniu testów jednostkowych (chyba że napiszesz niestandardowe potwierdzenie, które współdziała z twoją wiązką testową). Dzieje się tak często, ponieważ są one używane tam, gdzie należy zastosować odpowiednią obsługę błędów.
Przeważnie są one kompilowane z kompilacji wersji, co oznacza, że żadne z ich „testowania” nie jest dostępne, gdy uruchomisz kod, który faktycznie wypuszczasz; biorąc pod uwagę, że w sytuacjach wielowątkowych najgorsze problemy często pojawiają się tylko w kodzie wydania, może to być złe.
Czasami są kulą dla zepsutych projektów; tzn. konstrukcja kodu pozwala użytkownikowi wywoływać go w taki sposób, że nie powinien być wywoływany, a twierdzenie „zapobiega” temu. Napraw projekt!
Pisałem o tym więcej na moim blogu w 2005 roku tutaj: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html
źródło
Nie tyle zła, co generalnie przeciwne do zamierzonego. Istnieje oddzielenie między stałym sprawdzaniem błędów a debugowaniem. Assert sprawia, że ludzie myślą, że wszystkie debugowanie powinno być trwałe i powoduje ogromne problemy z czytelnością, gdy jest często używany. Obsługa błędów stałych powinna być lepsza niż tam, gdzie jest to konieczne, a ponieważ asert powoduje własne błędy, jest to dość wątpliwa praktyka.
źródło
nigdy nie używam assert (), przykłady zwykle pokazują coś takiego:
To źle, nigdy tego nie robię, co jeśli moja gra przydziela mnóstwo potworów? dlaczego powinienem zawiesić grę, zamiast tego powinieneś obsługiwać błędy z wdziękiem, więc zrób coś takiego:
źródło
new
nigdy nie wracanullptr
, rzuca.