Hello all,
When I try to put an object that contains not nil fields into a Table, ProveInit warnings appear.
I don't really need help with this per se, but I wanted to make it known that this is still an issue with the way that tables and/or the not nil checking are implemented.
As I try to write correct software, preventing accidental use of nil by using the not nil annotation seems initially like one of the easier things to do, but in practice there are some issues in the standard library. As another example, the & operator in the strfmt module doesn't return a string not nil, so I have to have some unreachable condition in my code. Right now this looks something like:
proc notNilOrDie(str: string): string not nil:
if not str.isNil:
return str
else:
/* abort, throw exception, somehow handle in a sane way ?? */
myObj.notNilField = notNilOrDie(&"{somevar}{another_var}")
What could be done to ensure that not nil is easier to work with? It seems that many avoid it altogether, based on the current Nim code on GitHub, and that doesn't seem quite right...
There are multiple issues with nil in Nim and I suppose we need to tackle them one after the other. My biggest gripe with nil is that seq and string have it, they shouldn't. The len implementation already treats nil as empty. What is left to do is to make add work with nil. Then the fact that we have nil for them should become an implementation detail. Eventually the internal implementation will be changed anyway. This means code like takeString(nil) will first produce a warning and then stop compiling. APIs that rely on a nil vs "" distinction need to be changed.
The second issue in Nim is its reliance on implicit default value initializations, the following is valid:
proc foo(): int = discard
echo foo() # produces 0
This is usually fine for types that have no "crashing" behaviour (integers, chars, etc) but it's bad for ref as nil causes these famous nil-deref bugs everywhere. One solution is to demand explicit initialization for any type that has nil. Then the only way nil can creep into your program is by writing nil.
If you additionally mark the type as not nil then derefs will be checked at compile-time to occur in a not-nil branch like the compiler tries to do today. But making nil explicit seems a nice middle-ground between rigidity and ease-of-use.
I like the idea of requiring manual ref initialization.
Anyway, I have a question. How should nil be made "explicit"? If a function can take a nil value, should there be an annotation of some sort? Around the forum I've seen suggestions such as:
proc canUseNil(s: string or nil)