5 ms·
Go helps devs by creating extra work like checking for inner nil and outer nil, hitting the "if err != nil { return nil, err }" button on their keyboard, using
by anonymoushn 4y ago
Go helps devs by creating extra work like checking for inner nil and outer nil, hitting the "if err != nil { return nil, err }" button on their keyboard, using reflection to instantiate unexported structs written by library authors who decided nobody should ever be able to make an interface-compatible wrapper that adds capabilities to their library, and relying on the language to enforce the checking of error return values, which it won't actually do if you don't assign any of the return values to variables.
- dgb23 4y agoLibrary authors not exporting their fundamental data structures is very painful, but this happens in many other languages too.
- throwaway894345 4y agoThere is no “inner/outer nil”. People just need to understand how interfaces work (they’re a reference type and sometimes they reference another reference type, so just like any language with nullable pointers/references, either the pointer or the pointee can be nil). 101 level stuff. There are valid criticisms of Go (I’m not a fan of nil in the first place, for example), but this inner/outer nil myth needs to die.
- anonymoushn 4y agoSure, if any method anywhere takes a pointer receiver, then anyone can declare an interface that matches that method, and that interface is (among other things) like an Option<Option<T>> in Rust or a ??T in Zig. It's weird to post "There is no inner/outer nil" since the most straightforward interpretation of that claim is that there is no None/Some(None) distinction, and this is straightforwardly not true. For some reason, most languages do not encourage the users to create types that are similar to Option<Option<T>>. Have you actually taken this stuff to a group of first-semester CS students and had them understand it and think it was a good design? In the stdlib there's this function: // String returns the source text used to compile the regular expression. func (re *Regexp) String() string { return re.expr } which takes a pointer-to-struct receiver and does not check it for nil before using it. So any program that declares a Stringable interface, diligently checks Stringables to make sure they are non-nil, then calls String() only on the non-nil ones is allowed to panic on this line from the stdlib because re == nil. The wonderful tutorial[0] teaches beginning go programmers to write the same sort of bug in the first lesson about interfaces. [0]: https://go.dev/tour/methods/9 https://go.dev/tour/methods/9
- throwaway894345 4y ago> the most straightforward interpretation of that claim is that there is no None/Some(None) distinction, and this is straightforwardly not true. That’s not the most straightforward interpretation, since it would invalidate the original criticism that I responded to (that Go more-or-less uniquely requires you to check inner/outer nil). > For some reason, most languages do not encourage the users to create types that are similar to Option<Option<T>>. Lots of languages “encourage” references to reference types (including Rust), but many of the ML family including Rust have non-nilable references. Go’s error (and that of many others including Python, Java, C, JS, etc) isn’t that it supports/encourages references to references, but rather that all references are nilable.
- anonymoushn 4y agoA difference between Go and Java is that a null Regex in Java will not be implicitly converted to a non-null Stringable containing a null Regex. If you'd like, I can post about "implicit conversion from nil pointers to non-nil interfaces containing nil pointers that can only be checked for nil using reflection, combined with interfaces that work by duck typing and an existing body of code that pervasively uses struct pointer receivers in methods that panic when passed a nil pointer, even if it was a nil pointer contained in a non-nil interface" but that's a bit of a mouthful.
- deleted 4y ago[deleted]
- throwaway894345 4y ago> A difference between Go and Java is that a null Regex in Java will not be implicitly converted to a non-null Stringable containing a null Regex. You have it exactly backwards--you're arguing that Go should implicitly convert a not-nil interface (that is implemented by a nil pointer) to a nil interface so that Go interfaces behave more like Java interfaces, but Go isn't doing any implicit conversions (unless you consider "assigning a concrete value to an interface" to be an "implicit conversion", but Java does this too). > implicit conversion from nil pointers to non-nil interfaces containing nil pointers Again, Go doesn't do any such implicit conversion, and you're arguing the position that it should. An interface is just a tuple containing a pointer to the data and a pointer to the type `(pdata, ptype)`. A nil interface is `(nil, nil)`, and an interface that is implemented by a nil pointer is `(nil, <pointer-to-concrete-type>)`. You're arguing the position that the latter should be implicitly converted to the former, but this would break code that uses nil as a valid value. The root cause of the problem isn't how Go handles nil pointers in interfaces, but rather that Go has nil pointers, interfaces, etc at all. Go gives you no way to express that a reference type can't be nil, but ideally it would have an enum system like Rust's that makes optionality an opt-in property via `Option<T>`. If an interface `Bar` is implemented by `*Foo` and neither `*Foo` nor `Bar` can be nil, then this problem largely goes away. If `Bar` is implemented by `Option<*Foo>`, then `Option<*Foo>`'s methods need to handle both the `Some(*Foo)` and `None` (i.e., nil) cases. > that can only be checked for nil using reflection You don't need reflection: i, ok := (iface).(*int) if !ok { fmt.Println("`iface` is not an `*int`") } if i == nil { fmt.Println("`iface` contains a nil pointer") } fmt.Printf("`iface` contains a pointer to `%d`", i)**