I don't want to descend down the rabbit hole of 'Go doesn't have generics', but as I said above, in this case, pointers do exactly what is needed.
The expressible values for a Nullable<T> are, in theory, the following:
A valid instance of T, or Null
The expressible values for a pointer-to-T are, in theory, the following:
A null pointer (pointer-to-0)
A pointer to a valid instance of T
A pointer to a previously valid, but now invalid because it was freed, instance of T
A pointer to an arbitrary location in memory
In systems programming (the space Go is presumably designed to be most useful for), the distinction between valid and invalid data is pretty important, so it's a little lazy to say 'just use pointers' when there are examples out there of safer, more efficient alternatives.
In C. Go has garbage collection, and no pointer arithmetic, so that's not the case in Go.
// p points to address 1000
p := (*int)(unsafe.Pointer(uintptr(1000)))
Of course, this is why use of the unsafe package is heavily discouraged.
The important distinction is that a 'Nullable' value type need not involve a pointer, which means it can entirely live on the stack, which dramatically limits the damage that can be done by corruption: Worst case, corruption sets the 'hasValue' flag to true, and you read an uninitialized struct off the stack. Much less catastrophic than a double-free or pointer into random memory (Though, of course, a determined attacker could probably make do with either).
Sure, I'm not saying that the extra allocation will always matter. In most cases it won't. My point is just that this type system workaround does cost some performance. For example, a heap-allocated struct that contains two nullable ints using the pointer trick has 3 heap allocations, not one.
> Sure you can - that's literally what the NullBool, etc. types do
But that has to be done for each type. If you define a custom type Foo and want a nullable version, you have to write the NullFoo boilerplate yourself. This is what generics are for.