A little irritating (but understandable) limitation on Go interfaces
I was all set to write an entry about how you could use Go-style interfaces to create what I called
easy, type-safe conversions from a variety of types to something you
wanted. The sketch of the problem and the idea is that suppose you
want to create an API that accepts arguments in multiple forms (eg), for example something in uncompiled string
form or in a compiled efficient form. The usual way to implement this is
with a type switch in your functions ('if argument is a string, convert
it to ...') but this annoying and limited.
Interfaces to the rescue, in theory: define a 'Converter' interface
with a single 'ToMyThing()' method, make your API take Converter
arguments (and your functions then call arg.ToMyThing()
), and
define a new ToMyThing()
method on strings, integers, and whatever
else you want to accept.
(The ToMyThing()
method for your actual type does nothing and just
returns itself.)
People who know Go are shaking their head sadly right about now. Here, let me tell you why:
prog.go:9: cannot define new methods on non-local type string
Well, oops. So much for that.
If you understand how Go's types and interfaces are implemented, this
makes sense. One of the parts of the type description for every concrete
Go type is a static, fixed array of methods (with various information
including their name and a function pointer); this is built as the type
is compiled. What this error message really means is 'the method array
for string
has already been built, you can't add entries to it now'.
It's not hard to see why allowing this would massively complicate Go's life. The big reason is that entries in the method array are sorted by name (for good reason). Adding a name to it after the type has been compiled means re-sorting the array, changing the index position of entries, and then finding and changing all references to now-invalid index positions in already compiled code. In practice you would want to defer the index position resolution until link time (as a form of link time relocation) and I'm not sure that's even possible in object formats like ELF. Certainly it would add a lot more complexity to the whole process.
(Actually it's even worse; you would have to defer building the method
table entirely until link time, since you might have to merge together
string
method definitions from all over your code base.)
You would also open up the possibility of weird link-time errors.
For example, suppose that two separate bits of Go code both
independently decide to add (different) Convert()
methods to
string
. This pretty much has to be an error, but it's something
you can only detect and report at link time. Worse, those bits of
Go code work independently; they only fail when you combine them
into a single program. This is not a recipe for good software
engineering and I'm
not at all surprised that Go left this out.
|
|