For Enum, it is not documented in the Odin Overview - Enumerations that nil value can be used as zero value of an Enum. So, I thought the integer 0 is the only zero value of Enum because Enum already has a zero value (integer 0), so there is no need for another zero value.
But the examples in Odin Overvew - or_return show that nil can be used to compare a Enum variant with value 0.
So, my questions are:
How does nil value fundamentally work?
Is is just 0 under the hood, i.e., the memory completely zeroed out?
I am not quite sure, but I think nil and 0 are equivalent for enums. There is also an open issue because of the ambiguity in switch cases.
The documentation/overview should be updated to explain and clarify the concept of nil behind enums and the difference to unions where nil means nothing/undefined aka absence of a value.
But the zero value for enums can also either be a real enum case or an invalid one, for example when the first case starts with 1 , or when the enum has no cases at all.
Well, you kind of already explained it yourself, but it’s basically a special value that represents the zero value for many types.
As far as I know, it always represents completely zeroed memory.
Anywhere where the type can be inferred to be something that can be nil. So if you look at that link you posted of nil slices, since the type inference expects a slice on the other side, it can infer that the nil should be a slice. Although it’s often better to just check the length since I think you can get valid zero-length slices with a nonzero pointer, but that’s beside the point.
{} can be used to get the zero value of any type, which is useful for types that can’t be nil and/or parapoly, otherwise nil is preferred due to being more specific.
The reason enums have a nil value is just so you can do if err != nil, as well as for the #shared_nil functionality of unions, which comes in handy if your errors are unions of enums.
The behaviour of nil in the example from the GitHub issue is quite confusing and can easily trip people up. This should be documented. I wouldn’t have known about the issue if you hadn’t mentioned it.
You get a zero-initialized memory block.
That block will represent the zero value for a given type.
The zero value is the default value.
Each type has its own zero value.
Zero Is Initialization (ZII):
Variables declared without an explicit initial value are given their zero value. Make the zero value useful:
The zero value should act as a useful default state for a variable.
And it works automatically. But… here’s the thing, as J.Blow likes to say.
With an enum type definition, Odin makes you responsible for following
or breaking this principle - whether that’s by design or by omission on Odin’s part.
A useful default state for an enum type is the value 0, which corresponds to its first field.
Odin lets you assign explicit values to enum fields.
And this is where you can choose to follow or break the principle.
The principle is broken when the value 0 does not correspond to any enum field.
This happens when:
There are no enum fields at all
You assign an explicit value to the first field only and that value is greater than 0
You don’t assign the value 0 to one of the enum fields
That’s why you see %!(BAD ENUM VALUE=0).
How does nil work? Let’s see some examples.
Zero value for cstring is an empty string. How do you check that? cstring_var == "", cstring_var == nil, cstring_var == {}.
Zero value for a dynamic array is when it has no elements, len and cap being 0.
You can check it like this: dyn_array_var == nil.
Zero value for an enum is the value 0 corresponding to its first field.
If you don’t break the principle you check it this way: enum_var == nil, enum_var == {}.
Otherwise, you can use reflect.enum_value_has_name(enum_var).
Zero value for rawptr, ^T, [^]T is a null pointer.
Check: pointer_var == nil, pointer_val == {}.
Zero value for a unionwith#no_nil is its first variant.
Check: union_var == {}. Zero value for a unionwithout#no_nil means no variant is used.
Check: union_var == nil, union_var == {}.
The takeaway from these examples should be this: nil is a predeclared identifier that represents (but is not necessarily) a zero value for some types.