3 ms·
It is a bit like void *outer = 0; // outer can be nil int val = 5; int *inner = &val; // inner can also be nil outer = (void*)(&inner) // can have an i
by kubb 5y ago
It is a bit like
void *outer = 0; // outer can be nil
int val = 5;
int *inner = &val; // inner can also be nil
outer = (void*)(&inner) // can have an inner value, inner nil or outer nil
- masklinn 5y agoIt's not really like that, this is two nested but different values which can be nil (not exactly independently but close). typed nils are a single value which is both nil and non-nil at the same time.
- throwaway894345 5y agoYou're mistaken. An interface is simply a reference type--like other reference types, it may refer to a type which is itself a reference type. The outer reference may be not-nil while the inner reference may be nil, which confuses people who don't understand references into believing that there is a single value which is both nil and not-nil at once. It's no different than `var x **int` (a variable called "x" whose type is a pointer to a pointer to an int). If you want to safely access the underlying integer data, it's not sufficient to check that the outermost reference is not nil--you must also verify that the inner reference is not nil. We run into this in every language with nullable reference types, including C, Java, Python, JavaScript, etc. EDIT: `*int` -> `**int`. TIL HN converts two asterisks into one.
- masklinn 5y ago> An interface is simply a reference type--like other reference types, it may refer to a type which is itself a reference type. The outer reference may be not-nil while the inner reference may be nil, which confuses people who don't understand references into believing that there is a single value which is both nil and not-nil at once. None of that is correct. The value-part of the fat pointer is the exact same in both situations: nil. It is a single value which is both nil and non-nil. > It's no different than `var x **int` I assume you meant that rather than what you actually wrote. It is in fact quite different. > If you want to safely access the underlying integer data, it's not sufficient to check that the outermost reference is not nil--you must also verify that the inner reference is not nil. Something a typed nil does not let you do, unless you know (or reflect) the underlying concrete type in order to downcast the interface back to a regular pointer. > We run into this in every language with nullable reference types, including C, Java, Python, JavaScript, etc. We really don't. In "every language" you have to actually create a double indirection for something within the same heavenly realm (but is not at all the same thing) to occur.
- throwaway894345 5y ago> I assume you meant that rather than what you actually wrote. Yes, today I learned HN replaces double-asterisks with single asterisks. Edited my original post. > None of that is correct. The value-part of the fat pointer is the exact same in both situations: nil. It is a single value which is both nil and non-nil. I repeat, an interface isn't "both nil and non-nil", but rather the nullity of the interface and the nullity of the underlying data are distinct propositions. For example, there is nothing analogous to "both nil and non-nil" when the runtime type is a value type, such as an integer. An interface behaves just like a reference. When the underlying data is also a reference, then it behaves like a reference to a reference. This means you can have a nil interface or a non-nil interface which references a nil pointer. Under the covers, Go implements this behavior by representing interfaces as a tuple where the first field contains a pointer to the runtime type information and the second field contains a pointer to the data. Go encodes a nil interface as (nil, nil) while it encodes an interface backed by a nil int pointer as `(<pointer to `int` type>, nil)`. > We really don't. In "every language" you have to actually create a double indirection for something within the same heavenly realm (but is not at all the same thing) to occur. Go interfaces to pointer data are functionally "double indirection". The outer pointer can be either nil or a pointer to the data which itself can be nil or valid. > Something a typed nil does not let you do, unless you know (or reflect) the underlying concrete type in order to downcast the interface back to a regular pointer. Well, this would be unsafe as the runtime type of the underlying data isn't guaranteed to be a pointer. So by default Go requires runtime type checks in this case, but you can always opt out of these runtime type checks via `unsafe` but this is usually inadvisable.