Anyone proficient in GO would care to provide the minimal code that illustrate that extremely weird behavior ? I find that post so disturbing that i still believe i've missed something.
Line 336. io.Copy takes a writer and a reader, but actually asserts whether they implement ReaderFrom/WriterTo, then use their methods, ReadFrom and WriteTo, if they do. (Same thing with the http package checking if the reader implements Closer, and calling Close if it does, though admittedly 'we should be able to call Close' is a much more unsafe assumption than 'if it implements ReadFrom, and it has the right type signature, we can just call that function instead'.)
The reason it doesn't take values implementing the interfaces ReaderFrom and WriterTo is that io.Copy works even if you don't implement those interfaces.
It's an unfortunate and regrettable predicament, but it's not something that comes up a lot. As others have mentioned in this thread, Go certainly isn't a language with referential transparency by any means--the type system provides minimal sanity checking, and the language itself is memory-safe, but it's not trying to be a "high-assurance language."
That is SO disturbing... They don't even check whether the parameter implements another interface, they do the check directly on the method by callin it... That's so ugly...
Reminds me of my surprise when i realized the math module only worked on float types, and that min/max only existed for some numerical types but not others. The thing in itself probably isn't a big deal, but it really questions the ability of the language to model complex relationships often found in business processes.
> That is SO disturbing... They don't even check whether the parameter implements another interface, they do the check directly on the method by callin it... That's so ugly...
That's incorrect. The code you see at the top:
if rt, ok := dst.(ReaderFrom); ok {
return rt.ReadFrom(src)
}
means: "If dst implements ReaderFrom, then call ReadFrom on this value of that type."
You can't arbitrarily call methods that may or may not exist. You can check if a value implements an interface by doing "newVal, ok := val.(suspectedInterface)", and checking if ok is true, or by doing "newVal := val.(suspectedInterface)" and living with a runtime panic if it doesn't implement the interface, then call a method in that interface on the new value newVal, which will be of type suspectedInterface. (If val doesn't implement suspectedInterface--i.e. ok is false--then newVal will be the zero value of suspectedInterface.)