Assert () vs enforce (): what to choose?

It’s hard for me to choose whether I should “force” the condition or “approve” the condition in D. (This is neutral for the language).

Theoretically, I know that you use assertions to look for errors, and you use other conditions to check for atypical conditions. For instance. you can say assert(count >= 0) for the argument of your method because it indicates an error with the caller and what you say enforce(isNetworkConnected) because it is not an error, it is just what you assume it might I really don’t believe in a legal situation that is beyond your control.

In addition, statements can be removed from the code as optimization without side effects, but enforcement actions cannot be removed, because they must always fulfill their condition code. Therefore, if I implement a container with lazy filling that fills the first time I access any of its methods, I say enforce(!empty()) instead of assert(!empty()) , because checking for empty() should always be done, since it is lazily executing code inside.

So, I think I know what they should mean. But theory is simpler than practice, and it’s hard for me to impose these concepts.

Consider the following:

I am making a range (similar to an iterator) that repeats the other two ranges and adds the results. (For functional programmers: I know that I can use map!("a + b") instead, but now I’m ignoring this, as this does not illustrate the question.) So I have code that looks like this in pseudo-code:

 void add(Range range1, Range range2) { Range result; while (!range1.empty) { assert(!range2.empty); //Should this be an assertion or enforcement? result += range1.front + range2.front; range1.popFront(); range2.popFront(); } } 

Should it be a statement or enforcement? (Is it a caller’s mistake that the ranges are not empty at the same time? Maybe he doesn’t control which range came from - he could come from the user - but again he still looks like an error, doesn't he whether?)

Or here is another pseudo-code example:

 uint getFileSize(string path) { HANDLE hFile = CreateFile(path, ...); assert(hFile != INVALID_HANDLE_VALUE); //Assertion or enforcement? return GetFileSize(hFile); //and close the handle, obviously } ... 

Should it be a statement or enforcement? The path may come from the user - so this may not be an error, but it is still a prerequisite of this method that the path must be valid. Do I approve or apply?

Thanks!

+4
source share
2 answers

I am not sure that it is completely neutral for the language. None of the languages ​​I use have enforce() , and if I were faced with what I did, I would like to use assert and enforce in the sense in which they were intended, which might be idiomatic for that language.

For example, assert in C or C ++ stops a program when it fails, it does not throw an exception, so its use may not be the same as what you are talking about. You do not use assert in C ++ unless you think that either the calling user made the error so serious that they cannot be relied on for cleaning (for example, passing a negative number), or any other code elsewhere made such a serious error that the program should be considered to be in the undefined state (for example, your data structure looks corrupt). However, C ++ distinguishes between runtime errors and logical errors, although this may roughly correspond, but I think that these are mostly preventable and inevitable errors.

In the case of add you should use a logical error if the author intends that a program that provides inconsistent lists has errors and needs to be fixed or thrown at runtime if this is only one of those things that can happen. For example, if your function should process arbitrary generators that do not necessarily have the ability to report their length without waiting for a destructive evaluation of the entire sequence, you most likely consider this an inevitable error condition.

A logical error call implies that it is the responsibility of the caller to check the length before calling add if they cannot provide this by using a pure reason. Thus, they will not be transmitted in the list from the user without explicitly checking the length in the first place, and, frankly, they consider themselves lucky, they even received an exception, and not undefined behavior.

A run-time error call expresses that it is "reasonably" (if abnormally) passed in lists of different lengths, except that this happened on this occasion. Therefore, I consider it rather forced than a statement.

In the case of filesize : for a file to exist, you should, if possible, consider this as a potential possible failure (forced execution), and not an error (statement). The reason is that the subscriber does not have the opportunity to make sure that the file exists - there is always someone with more privileges who can come and delete it, or unmount the entire file system, between checking for existence and calling filesize . Therefore, this is not necessarily a logical flaw in the calling code when it does not exist (although the end user could shoot himself in the leg). Because of this, there are likely to be subscribers who can view this as one of those things that happen, an inevitable error condition. Creating a file descriptor can also fail due to memory, which is another unavoidable error on most systems, although not necessarily recoverable if, for example, the over-commit function is enabled.

Another example to consider is operator[] vs. at() for a C ++ vector. at() throws out_of_range logical error, and not because it is inconceivable that the caller might want to recover, or because you must be kind of numbskull to make an error accessing the array outside the range using at() , and therefore, that the error is completely prevented if the caller wants to see it - you can always check size() before access, if you have no other way to find out if your index is good or not. And therefore, operator[] does not guarantee any checks at all, and in the name of efficiency, access outside the range has undefined behavior.

+1
source

I believe that you partially answered your question. Allegations are related to flow disruption. If your statement is incorrect, you will not agree to continue anything. If you apply something, you decide to allow something to happen depending on the situation. If you find that the conditions are not met, you can ensure that the entry in a particular section is rejected.

0
source

Source: https://habr.com/ru/post/1341330/