This page really threw me, for several reasons:
-
The text notes that there’s an error (so why don’t they fix it?)
-
The provided code doesn’t run (presumably because of the above error)
-
It’s not clear if this is a deliberate error to illustrate a point, or just a snafu
In addition, there’s no indication as to the purpose of the code, so it’s really hard to follow what it’s supposed to be illustrating. Is the type
interface declaration at the top deliberately before main
and all the subsequent type
and func
declarations? What’s the significance of this? Is there a significance of this?
A general confusion I’ve had with many of the Tour tutorials is the use of maths functions to illustrate concepts. I mean, I probably should know what this …
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
…should return, but it’s an added mental overhead to grokking what’s going on. Sometimes stuff has to be done with numbers but a lot of the illustrations around methods & functions could easily have been done with strings IMHO.
To get some kind of understanding of interfaces I found https://www.calhoun.io/how-do-interfaces-work-in-go/ and https://gobyexample.com/interfaces very good, and the latter links to https://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go which is also very useful and goes into a ton more detail.
The Golang Tour tutorials have been really clear up until this point, so I’m hoping that this was just an aberration :)
Exercise: Stringers 🔗
👉https://tour.golang.org/methods/18[A Tour of Go: Exercise: Stringers]
I quite enjoyed this one, even though I still had to Google for some help. I got that I needed a function to implement the String()
method for IPAddr
(which is there in the help text too but I didn’t notice, which goes to show I should spend longer reading the questions)
func (i IPAddr) String() string {
}
Within this I needed to take the four parts of the IP address and concatenate them with a .
separator. Feeling rather proud of myself I came up with this which nicely handled each part of the IP address:
func (i IPAddr) String() string {
a := ""
for v := range i {
a = a + v
}
return a
}
but failed:
./prog.go:15:9: invalid operation: a + v (mismatched types string and int)
So let’s try casting the type:
func (i IPAddr) String() string {
a := ""
for v := range i {
a = a + string(v)
}
return a
}
This executed successfully, but didn’t work:
loopback:
So let’s add some debug:
func (i IPAddr) String() string {
a := ""
for v := range i {
fmt.Printf("Value: %v %v\n", v, string(v))
a = a + string(v)
}
return a
}
This gives:
Value:
Value:
Value:
Value:
loopback:
So the value is showing as empty, which is odd, because we know it’s there. Let’s try more debug:
func (i IPAddr) String() string {
a := ""
for v := range i {
fmt.Printf("value: %v \tstring(value): %v\n", v, string(v))
a = a + string(v)
}
return a
}
value: 0 string(value):
value: 1 string(value):
value: 2 string(value):
value: 3 string(value):
OK, so the value we’re getting isn’t the IP address pieces… because we made a mistake in the for
statement and we’re getting the index, not the value. We’re also getting a blank for the string, but we’ll worry about that in a moment. Let’s fix the for
statement first. Using the underscore we can ignore the index and store the actual value in v
:
func (i IPAddr) String() string {
a := ""
for _, v := range i {
fmt.Printf("value: %v \tstring(value): %v\n", v, string(v))
a = a + string(v)
}
value: 127 string(value):
value: 0 string(value):
value: 0 string(value):
value: 1 string(value):
We’re getting somewhere.
What about this pesky blank string though when we try to cast the integer to a string? Courtesy of 6 Tips for Using Strings in Go and specifically the Convert ints (or any data type) into strings section I realised that string()
wasn’t the way to do it. What string()
is doing is returning the ASCII character of the given value. Check out the output if I bump up the value in the string
value in the Printf
:
func (i IPAddr) String() string {
a := ""
for _, v := range i {
fmt.Printf("value: %v \tstring(value+64): %v\n", v, string(v+64))
a = a + string(v)
}
return a
}
value: 127 string(value+64): ¿
value: 0 string(value+64): @
value: 0 string(value+64): @
value: 1 string(value+64): A
Maybe this was mentioned in the Tour and I missed it, but in doing the type conversion I’d referred back to Type conversions and it’s not covered there.
So instead of string()
we can use strconv or Sprintf
:
func (i IPAddr) String() string {
a := ""
for _, v := range i {
fmt.Printf("value: %v \tfmt.Sprintf(value): %v\n", v, fmt.Sprintf("%d",v))
a = a + string(v)
}
return a
}
value: 127 fmt.Sprintf(value): 127
value: 0 fmt.Sprintf(value): 0
value: 0 fmt.Sprintf(value): 0
value: 1 fmt.Sprintf(value): 1
Now we’re getting somewhere! Let’s use this Sprintf
in building the a
variable too, and add in a .
in the format string:
func (i IPAddr) String() string {
a := ""
for _, v := range i {
a = a + fmt.Sprintf("%d.",v)
}
return a
}
loopback: 127.0.0.1.
Look at that! We’re nearly there. Just the trailing .
to get rid of now, which a perusal of the strings
package turns up a function TrimRight
that should do the trick:
func (i IPAddr) String() string {
a := ""
for _, v := range i {
a = a + fmt.Sprintf("%d.",v)
}
return strings.TrimRight(a,".")
}
So the final code looks like this:
package main
import (
"fmt"
"strings"
)
type IPAddr [4]byte
func (i IPAddr) String() string {
a := ""
for _, v := range i {
a = a + fmt.Sprintf("%d.",v)
}
return strings.TrimRight(a,".")
}
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
And the output:
loopback: 127.0.0.1
googleDNS: 8.8.8.8
😃