我有一个实现 stringer 接口(interface)的类型
// RowID stores the ID of a single row in a table
type RowID []string
// String implements Stringer interface for RowID
func (r RowID) String() string {
return fmt.Sprintf("[%s]", strings.Join(r, ", "))
}
我有一个函数,我想将这种类型(或任何其他实现 Stringer 接口(interface)的类型)的 slice 传递给。
// PrintChanges ...
func PrintChanges(ids []fmt.Stringer) {
for _, id := range ids {
fmt.Println(id)
}
}
但是,go 编译器给我一个错误:
cannot use rowIDs (type []RowID) as type []fmt.Stringer in argument to PrintChanges
我可以将 RowID 传递给接受单个 fmt.Stringer 的函数
func PrintChange(id fmt.Stringer) {
fmt.Println(id)
}
...
PrintChange(RowID{"1", "24"})
但出于某种原因,我无法将 RowID 的一部分传递给接受 fmt.Stringer 的一部分的函数。我错过了什么?
Go Playground
保持简单
专业的 Go 程序员认为可以为每种类型重复这样的函数,或者对要打印的每个 slice 使用 for 循环。这是因为 Go 旨在尽可能易于阅读,即第一次阅读一大块代码的人不应该问“这个函数调用将转到哪个函数重载”之类的问题(C++ 中的常见陷阱, Go 没有函数重载)。所以你可以在 main()
中写:
Playground :https://ideone.com/IL3rGR
for _, id := range rowIDs { fmt.Println(id) }
简洁明了。
请注意 fmt.Println(id)
不会调用您的 String()
函数
这是因为 fmt
库使用 reflect
库并对您尝试替换的 string
类型的行为进行硬编码。 RowID
实例也是 string
实例,库总是更喜欢 string
而不是它的类型别名。我会说这是库中的错误:
图书馆来源:https://golang.org/src/fmt/print.go#L649
// Some types can be done without reflection.
switch f := arg.(type) {
...
case string:
p.fmtString(f, verb)
如果你真的想
您可以使用采用接口(interface){} 并使运行时反射(reflect)类型转换为 Stringer slice 的函数。请注意,这意味着您不会在编译期间看到类型不匹配,只会在运行时看到:
Playground :https://ideone.com/vlrBP9
func castToStringerSlice(iface interface{}) ([]fmt.Stringer, bool /* ok */) {
if reflect.TypeOf(iface).Kind() != reflect.Slice {
return nil, false
}
v := reflect.ValueOf(iface)
stringers := make([]fmt.Stringer, v.Len())
for i := 0; i < v.Len(); i++ {
stringers[i] = v.Index(i)
}
return stringers, true
}
func PrintChanges(iface_ids interface{}) {
ids, ok := castToStringerSlice(iface_ids)
if !ok {
log.Fatal(errors.New("the argument to PrintChanges must be a slice of Stringers"))
}
for _, id := range ids {
fmt.Println(id)
}
}
资源: