June 23rd, 2024

Interface Upgrades in Go (2014)

The article delves into Go's interface upgrades, showcasing their role in encapsulation and decoupling. It emphasizes optimizing performance through wider interface casting, with examples from io and net/http libraries. It warns about complexities and advises cautious usage.

Read original articleLink Icon
Interface Upgrades in Go (2014)

The article discusses interface upgrades in Go, focusing on how interfaces are used for encapsulation and decoupling in the language. It explains the concept of "interface upgrades," where interfaces can be cast to wider or unrelated interfaces if their dynamic types support it. Examples from the standard library, such as io and net/http, illustrate how interface upgrades can optimize performance by reducing unnecessary data copying. The article also highlights the benefits and challenges of interface upgrades, emphasizing the importance of using them sparingly and being cautious about potential pitfalls, especially for library authors and users. Overall, the article showcases how Go leverages interface upgrades to enhance functionality and performance in a standardized manner, while cautioning against unnecessary complexity when implementing custom interface upgrades.

Link Icon 10 comments
By @nickcw - 5 months
I used to use interface upgrades in rclone (a program which syncs your data to many different cloud providers) for all the optional features a backend (cloud provider) might have, for example renaming a file.

However the proxy problem became unmanageable. That's when I had a backend (eg the crypt backend which encrypts any other backend) which wraps another backend. Can the crypt backend rename things? That depends on what it is wrapping and you'd have to upgrade the interface call it and then get a special error to find out.

Eventually I switched to a table of bound method pointers which were easy to test against nil to see whether they were implemented and could be filled with a very small amount of reflection.

In my experience interface upgrades are useful but the proxy problem is very real so use sparingly only!

I did suggest at one point (to rsc) an addition to Go which would allow interface proxies to take methods away from their method set at run time to fix this problem, so the proxy could only have the methods it could implement. I've no idea how difficult this would be to implement though.

By @PUSH_AX - 5 months
I remember working in a team that used interfaces for a ton of things. Every single one of those things only ever had one concrete implementation, still to this day. A lot of those things were also purely for mocking in tests too.

Today I don’t use them, unless I need it retrospectively. Which I find is rare.

This is not a knock on interfaces as a language feature. Just inserting a random anecdote about pragmatism..

By @karmakaze - 5 months
This is basically a hack to get pragmatic performance at a cost of comprehensibility. It would have been better if Go allowed union types then a func could be declared to use interface A|B efficiently. Passing a narrow interface when a wider implemented one could get used is lying about the actual "interface" in the signature. Added to that is that Go's interfaces are structural (which I think are great), but could lead to accidental misuse by passing in interface A for an object that also has method X for unrelated purposes that co-incidentally satisfies interface B which the called func on A magically uses.

> Like all articles about Go’s interfaces, we are obligated to start with Go’s io package.

Also the io package, or stdlib in general is not a good place to look for good patterns to use in Go. Numerous antipatterns are used in the name of performance. The principles for stdlib authors and recommendation for Go developers are different. As an example io functions can return a value AND an error--and whether to continue or not depends on the specific error (as some are benign). It's better that I don't name an example as you should always be on the lookout for (until having learned) them.

By @rollulus - 5 months
Note that this is not necessarily a great thing to overuse. Also note that the article is 10 years old.

Relying on such upgrades sort of introduces a dark and fuzzy part of the API. Go’s http pkg is a notorious one, with how a http.ResponseWriter can also be a Flusher and Hijacker and I don’t know what else. If you in your middleware want to wrap it, you need to implement those interfaces as well, or the functionality is lost. But they’re not part of the “visible” API, they’re type assertions buried deep in the standard library, good luck with that. For this reason Go 1.20 introduced the http.ResponseController.

By @geoka9 - 5 months
> While it just so happens that all the ResponseWriters that net/http give you implement (e.g.) CloseNotifier, there’s not really any way for you to know that without reading the source.

Go editor tooling helps with this: gopls can do this nowadays (e.g. `lsp-find-implementation` in Emacs) and go oracle/guru may have already supported this back in 2014. It works both for finding implementations of an interface and finding interfaces implemented by a type.

By @dang - 5 months
Discussed at the time:

Interface Upgrades in Go - https://news.ycombinator.com/item?id=8714051 - Dec 2014 (40 comments)

By @McMini - 5 months
While the article highlights interface upgrades, it overlooks potential downsides like increased complexity in debugging. Anyone experienced issues with this?
By @MrBuddyCasino - 5 months
Am I missing something or is this a lot of words to describe the equivalent of the JVMs instanceof / type casting operator?
By @yawz - 5 months
The title needs "2014" to be added.
By @hmage - 5 months
November 5, 2014