为什么以下Go程序需要函数的名称为String()而不是其他名称?
I'm following a book which shows the following example.
package main
import (
"fmt"
)
const (
KB = 1024
MB = 1048576 //KB * 1024
GB = 1073741824 //MB * 1024
TB = 1099511627776 //GB * 1024
PB = 1125899906842624 //TB * 1024
)
type ByteSize float64
func (b ByteSize) String() string {
switch {
case b >= PB:
return "Very Big"
case b >= TB:
return fmt.Sprintf("%.2fTB", b/TB)
case b >= GB:
return fmt.Sprintf("%.2fGB", b/GB)
case b >= MB:
return fmt.Sprintf("%.2fMB", b/MB)
case b >= KB:
return fmt.Sprintf("%.2fKB", b/KB)
}
return fmt.Sprintf("%dB", b)
}
func main() {
fmt.Println(ByteSize(2048))
fmt.Println(ByteSize(3292528.64))
}
When I run this program it gives me the following output (in human readable data size units).
2.00KB
3.14MB
But when I change the name of the function called String() to anything else, or if I lower-case the S in String, it gives me the following output.
2048
3.29252864e+06
What is the reason behind that? Is there some String() function attached to some interface and our ByteSize type satisfies that interface? I mean what the hell?
When you define a method named String
with no parameters returning a string, you implement the Stringer
interface, which is documented here: https://golang.org/pkg/fmt/#Stringer.
Your last sentence is exactly right. In Go, you can satisfy interfaces you didn't even know existed. And you can write new interfaces for other people's functions. Their code does not have to write "implement" or derive or anything. It is entirely based on the function name and having a matching argument and return value list.
The example is very similar to an example in "Effective Go" by the Go Authors, and the meaning of the String()
method described there.
The ability to attach a method such as String to any user-defined type makes it possible for arbitrary values to format themselves automatically for printing. Although you'll see it most often applied to structs, this technique is also useful for scalar types such as floating-point types like ByteSize.