Relearning Go: Day 3
Maps to Strings & Runes
Maps
If the key doesn’t exist, the zero value of the value type is returned.
This rubs me up the wrong way. What’s the zero value for a file handle or a database connection?
I think zero values were a bad call in general.
There’s mention later of the optional second return value which says if the value was actually present or not. Better, but in the event that value was not found, Go still has to allocate that zero value which will, presumably, get thrown away. Maybe the compiler can optimise that away?
🤔 Is that optional second return value special magic, or can I use it in my code? Put another way: can I write a function that returns either 1 or 2 values depending on how the caller deconstructs the return value?
🤔 I am curious if it’s possible to use a struct, say, as a key.
Otherwise, nothing particularly surprising. Nice and simple.
Range
Briefly onwards, range
appears to be special for
-loop syntax to iterate over
arrays, slices, maps, and strings. There’s no general notion of iteration (yet;
I think it’s coming) so this has to suffice.
🤔 Can I create a type that works with range
, i.e. where for i in range valueOfMyType
means something?
Functions & Multiple Return Values & Variadic Arguments
gobyexample.com/functions
gobyexample.com/multiple-return-values
gobyexample.com/variadic-functions
These are unsurprising. What’s offered is a subset of that of many other mainstream languages – which is no bad thing, especially since keeping the language small is a theme of Go.
Closures & Recursion & Pointers
Having done a lot of Rust recently, the simplicity of Go’s closures is refreshing. Garbage collection takes care of a lot.
Recursion is also unsurprising.
Pointers provide some more interest because we learn that arguments are copied unless passed via a pointer. This could be a bit of a performance foot-gun when arguments are large. However, since the argument can also be mutated when passed as a pointer, the default to copy is likely to be less surprising, less likely to introduce bugs.
In Python I became accustomed to pass by reference. This simple and uniform mental model was a big part of why Python was easy to learn. But I did grow weary of not being able to say “look but don’t touch”; there’s no way to be sure that some function or one of the functions that it calls won’t mutate any given data structure.
🤔 Can one pass large arguments, e.g. a large array, by pointer in order to avoid the copy, but prevent the function from mutating the array too?
Strings and Runes
Strings and runes are familiar. I
like that Go’s rune
is actually a Unicode code point, and that range
works
with them. I bet that this has side-stepped a huge stream of bugs from dealing
with non-English texts. Got to love Go for doing that.
But this is Go. Can’t have a good choice without something to balance it out. The blog post Strings, bytes, runes and characters in Go says:
“Code point” is a bit of a mouthful, so Go introduces a shorter term for the concept: rune. The term appears in the libraries and source code, and means exactly the same as “code point”, with one interesting addition.
Minor niggle: I don’t think “code point” is a mouthful, plus it does align with Unicode’s nomenclature. “Rune” is cute but not helpful.
The Go language defines the word
rune
as an alias for the typeint32
, so programs can be clear when an integer value represents a code point.
Ugh 🤦 Programs can be clear… and they can also use rune
for signed
arithmetic, or pass code points into mathematical functions. Type safety has
been diluted here. Instead, make rune
opaque and not interchangeable with
integers. Even if one must use an integer, why int32
and not an unsigned
variant? This has the feel of a rushed API that didn’t receive enough thought
until it had to be retrospectively justified.
Sorry to leave it on a downer, but that’s enough for today. Next time I’ll pick up with structs.