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.<p>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.<p>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.<p>In my experience interface upgrades are useful but the proxy problem is very real so use sparingly only!<p>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.
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.<p>Today I don’t use them, unless I need it retrospectively. Which I find is rare.<p>This is not a knock on interfaces as a language feature. Just inserting a random anecdote about pragmatism..
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 <i>(which I think are great)</i>, 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.<p>> Like all articles about Go’s interfaces, we are obligated to start with Go’s io package.<p>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 <i>(as some are benign)</i>. <i>It's better that I don't name an example as you should always be on the lookout for (until having learned) them.</i>
Note that this is not necessarily a great thing to overuse. Also note that the article is 10 years old.<p>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.
> 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.<p>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.
Discussed at the time:<p><i>Interface Upgrades in Go</i> - <a href="https://news.ycombinator.com/item?id=8714051">https://news.ycombinator.com/item?id=8714051</a> - Dec 2014 (40 comments)
While the article highlights interface upgrades, it overlooks potential downsides like increased complexity in debugging. Anyone experienced issues with this?