Wandering Thoughts archives

2015-11-15

I should remember that I can cast things in Go

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.

programming/GoHasCasts written at 01:41:01; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.