Os Go
criadores do idioma escrevem :
Go não fornece afirmações. Eles são inegavelmente convenientes, mas nossa experiência tem sido de que os programadores os usam como muleta para evitar pensar em manipulação e relatório de erros adequados. O tratamento adequado de erros significa que os servidores continuam a operação após erros não fatais, em vez de travarem. O relatório de erros adequado significa que os erros são diretos e diretos, evitando que o programador interprete um grande rastreamento de falha. Erros precisos são particularmente importantes quando o programador que está vendo os erros não está familiarizado com o código.
Qual é a sua opinião sobre isso?
reflect.DeepEqual
, certamente não precisa . É conveniente, mas com o custo do desempenho (os testes de unidade são um bom caso de uso). Caso contrário, você poderá implementar qualquer verificação de igualdade apropriada para sua "coleção" sem muita dificuldade.for
loop no Go (assim como C). Ele iria ser muito bom ter operações fatia genéricos, embora a comparação fica complicada quando os ponteiros e estruturas estão envolvidos.Respostas:
Não, não há nada de errado
assert
contanto que você o use como pretendido.Ou seja, deveria ser para capturar casos que "não podem acontecer" durante a depuração, em oposição ao tratamento normal de erros.
fonte
Não, nem
goto
nemassert
são maus. Mas ambos podem ser mal utilizados.Afirmar é para verificações de sanidade. Coisas que devem matar o programa se não estiverem corretas. Não para validação ou como substituto para o tratamento de erros.
fonte
goto
sabiamente?goto
por razões puramente religiosas, depois use apenasgoto
ao invés de ofuscar o que você está fazendo. Em outras palavras: se você pode provar que realmente precisagoto
, e a única alternativa é encenar uma carga inútil de andaimes que, no final das contas, faz a mesma coisa, mas sem alterar a Polícia de Goto ... basta usargoto
. Obviamente, uma pré-condição disso é a parte "se você puder provar que realmente precisagoto
". Muitas vezes, as pessoas não. Isso ainda não significa que é inerentemente uma coisa ruim.goto
é usado em kernel do Linux para limpeza de códigoPor essa lógica, os pontos de interrupção também são maus.
As declarações devem ser usadas como um auxiliar de depuração e nada mais. "Mal" é quando você tenta usá-los em vez de manipular erros.
Existem declarações para ajudar você, o programador, a detectar e corrigir problemas que não devem existir e verificar se suas suposições permanecem verdadeiras.
Eles não têm nada a ver com o tratamento de erros, mas, infelizmente, alguns programadores os abusam como tal e os declaram "maus".
fonte
Eu gosto de usar afirmar muito. Acho muito útil quando estou criando aplicativos pela primeira vez (talvez para um novo domínio). Em vez de fazer uma verificação de erro muito sofisticada (que consideraria a otimização prematura), codifico rapidamente e adiciono muitas afirmações. Depois de saber mais sobre como as coisas funcionam, reescrevo e removo algumas das declarações e as altero para melhorar o tratamento de erros.
Por causa das declarações, passo muito menos tempo codificando / depurando programas.
Também notei que as declarações me ajudam a pensar em muitas coisas que podem interromper meus programas.
fonte
Como informação adicional, go fornece uma função interna
panic
. Isso pode ser usado no lugar deassert
. Por exemplopanic
imprimirá o rastreamento da pilha, portanto, de alguma forma, ele tem o objetivo deassert
.fonte
Eles devem ser usados para detectar erros no programa. Entrada ruim do usuário.
Se usado corretamente, eles não são maus.
fonte
Isso surge muito, e acho que um problema que torna as defesas de afirmações confusas é que elas geralmente são baseadas na verificação de argumentos. Portanto, considere este exemplo diferente de quando você pode usar uma asserção:
Você usa uma exceção para a entrada, porque espera receber algumas vezes informações incorretas. Você afirma que a lista está classificada para ajudá-lo a encontrar um bug em seu algoritmo, o que, por definição, você não espera. A asserção está apenas na compilação de depuração, portanto, mesmo que a verificação seja cara, você não se importa de fazê-lo em todas as chamadas da rotina.
Você ainda precisa testar seu código de produção, mas essa é uma maneira diferente e complementar de garantir que seu código esteja correto. Os testes de unidade garantem que sua rotina atenda à sua interface, enquanto as asserções são uma maneira mais refinada de garantir que sua implementação esteja fazendo exatamente o que você espera.
fonte
As afirmações não são más, mas podem ser facilmente mal utilizadas. Concordo com a afirmação de que "as asserções costumam ser usadas como muleta para evitar pensar em manipulação e relatório de erros adequados". Eu já vi isso com bastante frequência.
Pessoalmente, gosto de usar afirmações porque documentam as suposições que eu poderia ter feito ao escrever meu código. Se essas suposições forem quebradas ao manter o código, o problema poderá ser detectado durante o teste. No entanto, faço questão de remover todas as declarações do meu código ao fazer uma compilação de produção (por exemplo, usando #ifdefs). Ao eliminar as asserções na construção da produção, elimino o risco de alguém usá-las como muletas.
Há também outro problema com afirmações. As asserções são verificadas apenas no tempo de execução. Mas geralmente é o caso que a verificação que você gostaria de executar poderia ter sido executada em tempo de compilação. É preferível detectar um problema no momento da compilação. Para programadores C ++, o boost fornece BOOST_STATIC_ASSERT, que permite fazer isso. Para programadores C, este artigo ( texto do link ) descreve uma técnica que pode ser usada para executar asserções em tempo de compilação.
Em resumo, a regra de ouro que sigo é: Não use asserções em uma construção de produção e, se possível, use apenas asserções para coisas que não podem ser verificadas no tempo de compilação (ou seja, devem ser verificadas no tempo de execução).
fonte
Eu admito ter usado afirmações sem considerar o relatório de erros adequado. No entanto, isso não tira que eles sejam muito úteis quando usados corretamente.
Eles são especialmente úteis se você deseja seguir o princípio "Crash Early". Por exemplo, suponha que você esteja implementando um mecanismo de contagem de referência. Em certos locais do seu código, você sabe que a refcount deve ser zero ou um. E também suponha que, se o refcount estiver errado, o programa não travará imediatamente, mas durante o próximo ciclo de mensagens, nesse momento, será difícil descobrir por que as coisas deram errado. Uma declaração teria sido útil na detecção do erro mais próximo de sua origem.
fonte
Prefiro evitar código que faça coisas diferentes na depuração e liberação.
Quebrar o depurador em uma condição e ter todas as informações de arquivo / linha é útil, mas também a expressão exata e o valor exato.
Ter uma declaração que "avalie a condição apenas na depuração" pode ser uma otimização de desempenho e, como tal, útil apenas em 0,0001% dos programas - onde as pessoas sabem o que estão fazendo. Em todos os outros casos, isso é prejudicial, pois a expressão pode realmente alterar o estado do programa:
assert(2 == ShroedingersCat.GetNumEars());
faria o programa fazer coisas diferentes na depuração e liberação.Nós desenvolvemos um conjunto de macros de declaração que lançariam uma exceção e as faríamos na versão debug e release. Por exemplo,
THROW_UNLESS_EQ(a, 20);
lançaria uma exceção com a mensagem what () com arquivos, linhas e os valores reais de a, e assim por diante. Somente uma macro teria poder para isso. O depurador pode ser configurado para interromper o 'lançamento' do tipo de exceção específico.fonte
Não gosto de afirmações intensamente. Eu não iria tão longe quanto dizer que eles são maus.
Basicamente, uma afirmação fará o mesmo que uma exceção não verificada faria, a única exceção é que a afirmação (normalmente) não deve ser mantida para o produto final.
Se você criar uma rede de segurança para si mesmo durante a depuração e a construção do sistema, por que negaria essa rede de segurança para seu cliente, para o suporte técnico ou para qualquer pessoa que possa usar o software que está construindo no momento. Use exceções exclusivamente para afirmações e situações excepcionais. Ao criar uma hierarquia de exceção apropriada, você poderá discernir muito rapidamente um do outro. Só que desta vez a declaração permanece no local e pode fornecer informações valiosas em caso de falha que, de outra forma, seria perdida.
Portanto, entendo perfeitamente os criadores do Go removendo completamente as declarações e forçando os programadores a usar exceções para lidar com a situação. Há uma explicação simples para isso: as exceções são apenas um mecanismo melhor para o trabalho, por que ficar com as afirmações arcaicas?
fonte
Resposta curta: Não, acredito que asserções podem ser úteis
fonte
Recentemente, comecei a adicionar algumas declarações ao meu código, e é assim que tenho feito:
Divido mentalmente meu código em código de limite e código interno. Código de limite é um código que lida com a entrada do usuário, lê arquivos e obtém dados da rede. Nesse código, solicito entrada em um loop que sai somente quando a entrada é válida (no caso de entrada interativa do usuário) ou lança exceções no caso de dados corrompidos irrecuperáveis de arquivos / rede.
Código interno é tudo o resto. Por exemplo, uma função que define uma variável na minha classe pode ser definida como
e uma função que obtém entrada de uma rede pode ser lida da seguinte forma:
Isso me dá duas camadas de cheques. Qualquer coisa em que os dados sejam determinados no tempo de execução sempre recebe uma exceção ou tratamento imediato de erros. No entanto, esse check-in extra
Class::f
com aassert
declaração significa que, se algum código interno for chamadoClass::f
, ainda tenho uma verificação de integridade. Meu código interno pode não passar um argumento válido (porque eu posso ter calculado avalue
partir de uma série complexa de funções), então eu gosto de ter a asserção na função de configuração para documentar que, independentemente de quem está chamando a função,value
não deve ser maior que ou igual aend
.Isso parece se encaixar no que estou lendo em alguns lugares, que afirmações devem ser impossíveis de violar em um programa que funcione bem, enquanto exceções devem ser para casos excepcionais e errôneos que ainda são possíveis. Como, em teoria, estou validando todas as informações, não deve ser possível que minha afirmação seja acionada. Se for, meu programa está errado.
fonte
Se as afirmações sobre as quais você está falando significam que o programa vomita e depois existe, as afirmações podem ser muito ruins. Isso não quer dizer que eles estejam sempre a coisa errada a usar, eles são um construto que é facilmente usado mal. Eles também têm muitas alternativas melhores. Coisas assim são boas candidatas a serem chamadas de más.
Por exemplo, um módulo de terceiros (ou qualquer módulo realmente) quase nunca deve sair do programa de chamada. Isso não dá ao programador responsável o controle sobre o risco que o programa deve assumir naquele momento. Em muitos casos, os dados são tão importantes que até salvar dados corrompidos é melhor do que perdê-los. As declarações podem forçar você a perder dados.
Algumas alternativas para afirmações:
Algumas referências:
Mesmo as pessoas que defendem a afirmação acham que devem ser usadas apenas no desenvolvimento e não na produção:
Essa pessoa diz que as declarações devem ser usadas quando o módulo potencialmente corromper dados que persistem após uma exceção ser lançada: http://www.advogato.org/article/949.html . Este é certamente um ponto razoável, no entanto, um módulo externo nunca deve prescrever a importância dos dados corrompidos para o programa de chamada (saindo "para" eles). A maneira correta de lidar com isso é lançando uma exceção que deixa claro que o programa agora pode estar em um estado inconsistente. E como os bons programas consistem principalmente em módulos (com um pouco de código de cola no executável principal), afirmações são quase sempre a coisa errada a se fazer.
fonte
assert
é muito útil e pode poupar muito tempo de retorno quando erros inesperados ocorrem ao interromper o programa nos primeiros sinais de problemas.Por outro lado, é muito fácil abusar
assert
.A versão correta e correta seria algo como:
Então ... a longo prazo ... no quadro geral ... Devo concordar que isso
assert
pode ser abusado. Eu faço isso o tempo todo.fonte
assert
está sendo abusado por manipulação de erros porque é menos digitado.Portanto, como designers de linguagem, eles devem ver que o tratamento adequado de erros pode ser feito com digitação ainda menor. Excluir a declaração porque seu mecanismo de exceção é detalhado não é a solução. Oh, espere, o Go também não tem exceções. Que pena :)
fonte
Tive vontade de chutar a cabeça do autor quando vi isso.
Uso afirmações o tempo todo no código e eventualmente as substituo quando escrevo mais código. Uso-os quando não escrevi a lógica necessária e quero ser alertado quando executo o código em vez de escrever uma exceção que será excluída à medida que o projeto se aproxima da conclusão.
Exceções também se misturam com o código de produção mais facilmente, o que eu não gosto. Uma afirmação é mais fácil de notar do que
throw new Exception("Some generic msg or 'pretend i am an assert'");
fonte
Meu problema com essas respostas na defesa de afirmação é que ninguém especifica claramente o que a torna diferente de um erro fatal regular e por que uma afirmação não pode ser um subconjunto de uma exceção . Agora, com isso dito, e se a exceção nunca for detectada? Isso torna uma afirmação por nomenclatura? E, por que você desejaria impor uma restrição no idioma que pode ser gerada uma exceção que / nada / possa lidar?
fonte
func()
com um número negativo. Agora, de repente, com suas afirmações, você puxou o tapete debaixo de mim e não me deu uma chance de se recuperar, em vez de me dizer educadamente o que estou solicitando, não pode ser feito. Não há nada errado em acusar o programa de se comportar mal e emitir uma citação: o problema é que você está confundindo o ato de fazer cumprir uma lei e condenar o criminoso.Sim, afirmações são más.
Muitas vezes, eles são usados em locais onde o tratamento adequado de erros deve ser usado. Acostume-se a escrever o tratamento adequado de erros de qualidade de produção desde o início!
Geralmente eles atrapalham a escrita de testes de unidade (a menos que você escreva uma afirmação personalizada que interaja com seu equipamento de teste). Isso geralmente ocorre porque eles são usados onde o tratamento adequado de erros deve ser usado.
Principalmente, eles são compilados a partir das versões do release, o que significa que nenhum dos "testes" deles está disponível quando você está executando o código que realmente lança; dado que em situações multithread, os piores problemas geralmente aparecem apenas no código do release, isso pode ser ruim.
Às vezes, são uma muleta para projetos quebrados; isto é, o design do código permite que um usuário o chame de uma maneira que não deva ser chamado e a afirmação "impede" isso. Corrija o design!
Eu escrevi sobre isso mais no meu blog em 2005 aqui: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html
fonte
Não tanto mal como geralmente contraproducente. Há uma separação entre verificação de erro permanente e depuração. A afirmação faz com que as pessoas pensem que toda a depuração deve ser permanente e causa grandes problemas de legibilidade quando muito usada. O tratamento permanente de erros deve ser melhor do que o necessário, e, como a afirmação causa seus próprios erros, é uma prática bastante questionável.
fonte
Eu nunca uso assert (), exemplos geralmente mostram algo como isto:
Isso é ruim, eu nunca faço isso, e se o meu jogo estiver alocando um monte de monstros? Por que eu deveria travar o jogo? Em vez disso, você deve lidar com os erros normalmente, então faça algo como:
fonte
new
nunca voltanullptr
, joga.