I think of Go as a strongly typed language. My broad and somewhat reflexive view of strongly typed languages is that they mostly don't allow you to cast things around because most casts will require expensive data conversion and the language wants you to do that explicitly, with your own code. Go even sticks to this view; you can cast numbers around (because it's too useful) and you can go between
string
and
[]byte
(because it's a core operation), and that's mostly it.
(Then there's interfaces, which expose some tricks . Interface casting involves a bunch of potentially expensive magic, but it's a core feature of Go so it's an exception to the 'no expensive operations via casts' rule of thumb.)
However, there is an important practical exception to this, which comes about because of another thing that Go encourages: lots of named types that you derive from fundamental types. Rather than using, say,
int
, for all sorts of different things in your code, everyone knows that you should instead create various specific types:
type Phase int type Action int type OneMap map[string]string type TwoMap map[string]string
This way you can never accidentally use a
Phase
when the actual function, field, or whatever is supposed to be an
Action
, or pass a
OneMap
function a
TwoMap
, and so on. Go's strong typing will force them to be separate (even if this is sometimes irritating, for example if you're dealing with cgo ).
These derived types can be cast to each other and to their underlying type. This is not just if they're numbers; any derived type can be cast around like this, provided that the underlying 'real' types are the same (per the Conversions section of the language spec ).
(At a mechanical level it's easy to see why this is okay; since the two derived types have exactly the same memory layout, you don't have to do expensive conversion to generate a type-safe result.)
Now, ordinarily you still don't want to cast a
OneMap
to a
TwoMap
(or to a
map[string]string
). But there is one special case that matters to me, and that's if I want to do the same operation on both sorts of maps. Since I actually can cast them around, I don't need to write two duplicated blocks of (type-specific) code to do the same operation. Instead I can write one, perhaps one that's generic to the
map[string]string
type, and simply call it for both cases through casts. This is not the only way to create common code for a generic operation but it's probably the easiest one to add on the fly without a bunch of code refactoring.
So this is why I need to remember that casting types, even complex types, is something that I can do in Go. It's been kind of a reflexive blind spot in my Go code in the past, but hopefully writing this will avoid it in the future.