Why can't I do fmt.Sprintf ("% d.% D.% D.% D", a ...)?

I'm learning Go and I'm stuck on Go Tour (exercise-stringer.go: https://tour.golang.org/methods/7 ).

Here is the code:

type IPAddr [4]byte // TODO: Add a "String() string" method to IPAddr. func (a IPAddr) String() string { return fmt.Sprintf("%d.%d.%d.%d", a...) } 

So, I realized that the internal representation of IPAddr is [4]byte , so the distribution operator works. But I get:

 cannot use []string literal (type []string) as type []interface {} in argument to fmt.Sprintf 

What the hell? String slice doesn't work either, what's going on here?

EDIT . Sorry, there was an error in my question: the error was about type IPAddr , not []string . I was playing with the code and I inserted the wrong output. Anyway, thanks to peterSO and 0x434D53 about the invariance of slices in Go.

Well, that begs another question. Why is this implemented in this way? I assume that you will only have an Iterable interface, so any implementation will work.

Sidenote : when I first heard about Go, this bold statement “compiled but expressive” appeared. And an explicit interface implementation is a great example of this, but things like explicit conversion, no operator overloading, etc., give me a "90s Java feel." Which is sad because Go seems like a great language.

+5
source share
6 answers

Walk along the route

Exercise: Stringers

Make IPAddr type IPAddr to print the address as a dotted square.

For example, IPAddr{1, 2, 3, 4} should print as "1.2.3.4 ."

 package main import "fmt" type IPAddr [4]byte // TODO: Add a "String() string" method to IPAddr. func main() { addrs := map[string]IPAddr{ "loopback": {127, 0, 0, 1}, "googleDNS": {8, 8, 8, 8}, } for n, a := range addrs { fmt.Printf("%v: %v\n", n, a) } } 

There is no implicit conversion of []string to []interface {} . See Conversions in the Go Programming Language Specification . An explicit conversion is necessary. For instance,

 package main import "fmt" type IPAddr [4]byte // A "String() string" method for IPAddr. func (a IPAddr) String() string { return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3]) } func main() { addrs := map[string]IPAddr{ "loopback": {127, 0, 0, 1}, "googleDNS": {8, 8, 8, 8}, } for n, a := range addrs { fmt.Printf("%v: %v\n", n, a) } } 

Output:

 loopback: 127.0.0.1 googleDNS: 8.8.8.8 
+6
source

From the go language specification:

If f is variational with a finite parameter p of type ... T, then inside f the type p is equivalent to the type [] T

But in Go segments and arrays, the type invariant. Thus, []T is different from []U if T and U are different types. They are not related at all, even if T is a structural subtype of U Therefore, []string not a []interface .

+2
source

As pointed out by go faq , an implicit conversion from a typed array to the []interface {} .

The following solution works, but requires an intermediate slice:

 func (ip IPAddr) String() string { tmp := make([]interface{}, len(ip)) for i, val := range ip { tmp[i] = val } return fmt.Sprintf("%d.%d.%d.%d", tmp...) } 
+2
source

You need to implement this method for the Stringer interface.

 func (ip IPAddr) String() string { return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3]) } 
0
source

range should be used to iterate over the array, so the answer will look like this:

 func (ip IPAddr) String() string { out := fmt.Sprintf("%v", ip[0]) for _, value := range ip[1:] { out += fmt.Sprintf(".%v", value) } return out } 
0
source

First, when I run: main package

import "fmt"

 type IPAddr [4]byte // TODO: Add a "String() string" method to IPAddr. func (a IPAddr) String() string { return fmt.Sprintf("%d.%d.%d.%d", a...) } func main() { addrs := map[string]IPAddr{ "loopback": {127, 0, 0, 1}, "googleDNS": {8, 8, 8, 8}, } for n, a := range addrs { fmt.Printf("%v: %v\n", n, a) } } 

Error:

prog.go: 9: cannot use (type IPAddr) as type [] interface {} in argument fmt.Sprintf

but not

cannot use [] a string literal (type [] string) as type [] interface {} in the argument fmt.Sprintf

So, I think something went out of sync when copying and pasting.

type IPAddr [4]byte does not define a string, so the error message in the question is misleading.

This is a [4]byte , a completely different type (in terms of the Go language type) of the string. This is not []byte .

Otherwise, type IPAddr [4]byte satisfies the interface, for example, implements String (), which can use fmt.Sprintf, because the IPAddr String () method does not compile.

You can try to convert [4]byte to string, but this string(a) conversion is not legal. Worse, four byte values ​​will be treated as character codes and will not be converted to the symbolic representation of four small integer values. It is very likely that some byte values ​​of IPAddr may be invalid UTF-8, which would be even stranger if the program tried to print it.

As explained in other answers,
return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3])
returns the string value of IPAddr in the format you are aiming for.

Once func (a IPAddr) String() string valid, it works; IPAddr implements the fmt.Stringer interface.

Then %v at
fmt.Printf("%v: %v\n", n, a)
can be replaced by %s in
fmt.Printf("%s: %s\n", n, a)
because the fmt output methods have an implementation of String ().

I prefer %s to %v because it signals that the program does not rely on the "default representation of Go" (ie, for this array [127 0 0 1] ) and that the type implements String ().

-2
source

Source: https://habr.com/ru/post/1234802/


All Articles