我正在学习Go和我一直坚持Go巡演(exercise-stringer.go:https://tour.golang.org/methods/7)。
这里有一些代码:
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...)
}
所以我认为IPAddr
的内部表示是[4]byte
,因此传播运算符起作用。但是我得到了:
cannot use []string literal (type []string) as type []interface {} in argument to fmt.Sprintf
到底是什么?字符串切片也不起作用,这里发生了什么?
编辑:抱歉,我的问题出现了错误 - 错误与IPAddr
类型有关,而不是[]string
。我正在玩代码而且我粘贴了错误的输出。无论如何,感谢peterSO和0x434D53关于Go中切片的不变性。
嗯,这提出了另一个问题。为什么以这种方式实施?我想你只是有一些Iterable
接口,所以任何实现它的结构都会#34;只是工作"。
旁注:当我第一次听说Go时,有一个大胆的声明"编译,但富有表现力"。显式接口实现就是这方面的一个很好的例子,但是显式转换,缺少运算符重载等等都会给我带来Java感觉" 90。这很难过,因为Go看起来很棒。
答案 0 :(得分:8)
练习:Stringers
使
IPAddr
类型工具fmt.Stringer
将地址打印为 点缀四边形。例如,
IPAddr{1, 2, 3, 4}
应打印为"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) } }
没有[]string
到[]interface {}
的隐式转换。请参阅A Tour of Go中的Conversions。您需要提供显式转换。例如,
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)
}
}
输出:
loopback: 127.0.0.1
googleDNS: 8.8.8.8
答案 1 :(得分:3)
正如go faq中所述,没有从类型数组到[]interface {}
的隐式转换。
以下解决方案有效但需要创建中间切片:
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 :(得分:2)
从go语言规范:
如果f是可变参数且最终参数p的类型为...... T,那么在f中p的类型等同于类型[] T
但是在Go切片和数组中类型是不变的。因此[]T
与[]U
不同,如果T
和U
是不同的类型。它们完全没有关系,即使T
是U
的结构子类型。因此[]string
不是[]interface
。
答案 3 :(得分:0)
您需要为Stringer接口实现此方法。
func (ip IPAddr) String() string {
return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3])
}
答案 4 :(得分:0)
应该使用range
来迭代数组,所以答案如下:
func (ip IPAddr) String() string {
out := fmt.Sprintf("%v", ip[0])
for _, value := range ip[1:] {
out += fmt.Sprintf(".%v", value)
}
return out
}
答案 5 :(得分:0)
func (ip IPAddr) String() string {
var s []string;
for _, v := range ip {
s = append(s, fmt.Sprintf("%v", v))
}
return strings.Join(s, ".")
}
答案 6 :(得分:-2)
首先,当我'跑'时: 包主要
导入“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)
}
}
错误是:
prog.go:9:不能使用(类型IPAddr)作为类型[] interface {} fmt.Sprintf的参数
而不是
不能使用[] string literal(type [] string)作为类型[] interface {} fmt.Sprintf的参数
所以,我认为复制和粘贴时会出现一些不同步的现象。
type IPAddr [4]byte
没有定义字符串,因此问题中的错误消息会产生误导。
它是一个[4]byte
,一个完全不同的类型(从Go语言类型的角度来看)来自字符串。它也不是[]byte
。
type IPAddr [4]byte
也不满足fmt.Sprintf可以使用的接口,例如实现String(),因为IPAddr的String()方法不会被编译。
您可能会尝试将[4]byte
转换为字符串,但转换string(a)
不合法。更糟糕的是,四字节值将被视为字符代码,而不会转换为4个小整数值的字符表示。一些IPAddr字节值很可能是无效的UTF-8,如果程序试图打印它会更加奇怪。
如其他答案所述,
return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3])
以您要瞄准的格式返回IPAddr的字符串值。
一旦func (a IPAddr) String() string
有效,它就有效; IPAddr
实现了fmt.Stringer接口。
然后%v
在
fmt.Printf("%v: %v\n", n, a)
可以由中的%s
替换
fmt.Printf("%s: %s\n", n, a)
因为fmt输出方法具有String()的实现。
我更喜欢%s
到%v
因为它表示程序不依赖于'默认Go值表示'(即对于该数组[127 0 0 1]
),并且该类型实现串()。