5 go
代码A:
package main
import "fmt"
func main() {
slice := IntSlice{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(slice)
}
type IntSlice []int
Run Code Online (Sandbox Code Playgroud)
输出A:
[0 1 2 3 4 5 6 7 8 9]
Run Code Online (Sandbox Code Playgroud)
代码B:
package main
import "fmt"
func main() {
slice := IntSlice{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(slice)
}
type IntSlice []int
func (slice IntSlice) Error() string { return "this is called." }
Run Code Online (Sandbox Code Playgroud)
输出B:
this is called.
Run Code Online (Sandbox Code Playgroud)
为什么fmt.Println(slice)这两个代码(A和B)的行为不同?
或为什么自动fmt.Println(slice)打电话slice.Error()?
这被记录为fmt(强调我的)行为的一部分:
除非使用动词%T和%p打印,否则特殊格式注意事项适用于实现某些接口的操作数.按申请顺序:
如果操作数是一个reflect.Value,则操作数将替换为它保存的具体值,并继续打印下一个规则.
如果操作数实现Formatter接口,则将调用它.Formatter提供格式化的精细控制.
如果%v动词与#flag(%#v)一起使用,并且操作数实现了GoStringer接口,则将调用该接口.
如果格式(对于Println等隐式%v)对字符串有效(%s%q%v%x%X),则适用以下两个规则:
如果操作数实现了错误接口,则将调用Error方法将对象转换为字符串,然后根据动词的需要对其进行格式化(如果有).
如果操作数实现方法String()字符串,则将调用该方法将对象转换为字符串,然后根据动词(如果有)格式化该字符串.
对于复合操作数(如切片和结构),格式以递归方式应用于每个操作数的元素,而不是作为整体应用于操作数.因此,%q将引用一个字符串片段的每个元素,%6.2f将控制浮点数组的每个元素的格式.
所述fmt包看到该slice工具error和打印value.Error(),而不是遍历切片以及将格式化到每个元素.