←back to thread

Go subtleties

(harrisoncramer.me)
235 points darccio | 1 comments | | HN request time: 0.241s | source
Show context
rowanseymour ◴[] No.45666258[source]
Ah the old nil values boxed into non-nil interfaces. Even after 8 years writing go code almost every day this still bites me occasionally. I've never seen code that actually uses this. I understand why it is the way it is but I hate it.
replies(4): >>45666614 #>>45666792 #>>45666859 #>>45669163 #
kitd ◴[] No.45666859[source]
The advice I've read (and follow) is always to return values, not interfaces, from functions and test for nil against them. That IME tends to nip the majority of nil interface problems in the bud.
replies(4): >>45667017 #>>45667105 #>>45667244 #>>45667694 #
leetrout ◴[] No.45667105[source]
Return concrete types, accept interfaces. Returning interfaces hides behavior and hampers evolution; accept interfaces so callers can swap implementations. For testing, mock the dependency, not the return value.

Loudest arguments against returning concrete types were on the terraform core team and the excuse was it makes testing easier. I disagree.

replies(1): >>45667214 #
lenkite ◴[] No.45667214[source]
This advice of "returning concrete types" is in most cases a horrible anti-pattern that prevents evolution due to lack of information hiding. It has been also deliberately broken in the standard library in several places. This "advice" cannot be generically applied. Places where it is has been deliberately broken:

   net.Dial (Conn, error) 
   image.Decode(r io.Reader) (Image, string, error)
   sha256.NewXXX() hash.Hash
   flate.NewReader(r io.Reader) io.ReadCloser
   http.NewFileTransport(fs FileSystem) RoundTripper
Regarding `os.File`, the Go team even said: “If we were starting from scratch, we might do it differently.”

That’s why Go added abstractions later like fs.FS and fs.File.

   embed/fs.Open again deliberately breaks this.
Whereas consider its counterpart net.Conn. net.Conn is one of the most successful interfaces in the Go standard library. It’s the foundation of the net, net/http, tls, and net/rpc packages, and has been stable since Go 1.0. It didn't need a replacement fs.Fs.

If you will always only ever have one implementation in absolute permanence and no mocking/fake/alternative implementation is ever required in eternity, return a concrete type. Otherwise, consider whether returning an interface makes more sense.

replies(2): >>45667420 #>>45667824 #
1. leetrout ◴[] No.45667824[source]
Like anything else there are exceptions to the rule. Pointing to the standard library is a weak position because it is consistently inconsistent.

Go's standard library interfaces (like net.Conn) earned their place.

Premature interfaces calcify mistakes and that's what the guideline pushes back on.