Learning Golang (some rough notes) - S01E05 - Interfaces
Published: 2020-07-01 15:18:11

👉[A Tour of Go: Exercise: Stringers](https://tour.golang.org/methods/18)

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)
    
```golang    
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:
    
```golang    
func (i IPAddr) String() string {
    a := ""
    for v := range i {
        a = a + v
    }
    return a
}


./prog.go:15:9: invalid operation: a + v (mismatched types string and int)
```

So let's try casting the type:
    
```golang    
func (i IPAddr) String() string {
    a := ""
    for v := range i {
        a = a + string(v)
    }
    return a
}
```

This executed successfully, but didn't work:

So let's add some debug:
    
```golang    
func (i IPAddr) String() string {
    a := ""
    for v := range i {
        fmt.Printf("Value: %v %vn", v, string(v))
        a = a + string(v)
    }
    return a
}
```

```
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:
    
```golang    
func (i IPAddr) String() string {
    a := ""
    for v := range i {
        fmt.Printf("value: %v tstring(value): %vn", 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`:
    
```golang       
func (i IPAddr) String() string {
    a := ""
    for _, v := range i {
        fmt.Printf("value: %v tstring(value): %vn", 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][1] 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`:
    
```golang        
func (i IPAddr) String() string {
    a := ""
    for _, v := range i {
        fmt.Printf("value: %v tstring(value+64): %vn", 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][2] and it's not covered there. So instead of `string()` we can use [strconv][3] or [`Sprintf`][4]:
    
    
```golang    
func (i IPAddr) String() string {
    a := ""
    for _, v := range i {
        fmt.Printf("value: %v tfmt.Sprintf(value): %vn", 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:
    
```golang        
func (i IPAddr) String() string {
    a := ""
    for _, v := range i {
        a = a + fmt.Sprintf("%d.",v)
    }
    return a
}
```

Look at that! We're nearly there. Just the trailing `.` to get rid of now, which a perusal of the [`strings`][5] package turns up a function [`TrimRight`][6] that should do the trick:
    
```golang        
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:
    
    
```golang    
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: %vn", name, ip)
    }
}
```

```
loopback: 127.0.0.1
googleDNS: 8.8.8.8
```

[1]: https://www.calhoun.io/6-tips-for-using-strings-in-go/
[2]: https://tour.golang.org/basics/13
[3]: https://golang.org/pkg/strconv/
[4]: https://golang.org/pkg/fmt/#Sprintf
[5]: https://golang.org/pkg/strings/
[6]: https://golang.org/pkg/strings/#TrimRight