1. Go 常量溯源

Go 中所有与常量有关的声明都通过 const 来进行,例如:

  1. // $GOROOT/src/os/file.go
  2. const (
  3. // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
  4. O_RDONLY int = syscall.O_RDONLY // open the file read-only.
  5. O_WRONLY int = syscall.O_WRONLY // open the file write-only.
  6. O_RDWR int = syscall.O_RDWR // open the file read-write.
  7. // The remaining values may be or'ed in to control behavior.
  8. O_APPEND int = syscall.O_APPEND // append data to the file when writing.
  9. O_CREATE int = syscall.O_CREAT // create a new file if none exists.
  10. O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
  11. O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
  12. O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
  13. )

绝大多数情况下,Go 常量在声明时并不显式指定类型,也就是说使用的是无类型常量(untyped constants)。比如:

  1. // $GOROOT/src/io/io.go
  2. // Seek whence values.
  3. const (
  4. SeekStart = 0 // seek relative to the origin of the file
  5. SeekCurrent = 1 // seek relative to the current offset
  6. SeekEnd = 2 // seek relative to the end
  7. )

2. 有类型常量带来的“烦恼”

不可以被相互比较或混在一个表达式中进行运算:

  1. type myInt int
  2. func main() {
  3. var a int = 5
  4. var b myInt = 6
  5. fmt.Println(a + b) // invalid operation: a + b (mismatched types int and myInt)
  6. }

必须进行显式地转型:

  1. type myInt int
  2. func main() {
  3. var a int = 5
  4. var b myInt = 6
  5. fmt.Println(a + int(b)) // 输出:11
  6. }

有类型常量与变量混合在一起进行运算求值时也要遵循这一要求:

  1. type myInt int
  2. const n myInt = 13
  3. const m int = n + 5 // cannot use n + 5 (type myInt) as type int in const initializer
  4. func main() {
  5. var a int = 5
  6. fmt.Println(a + n) // invalid operation: a + n (mismatched types int and myInt)
  7. }

唯有通过显式转型才能让上面代码正常工作:

  1. type myInt int
  2. const n myInt = 13
  3. const m int = int(n) + 5
  4. func main() {
  5. var a int = 5
  6. fmt.Println(a + int(n)) // 输出:18
  7. }

3. 无类型常量消除烦恼,简化代码

  1. const (
  2. a = 5
  3. pi = 3.1415926
  4. s = "Hello, Gopher"
  5. c = 'a'
  6. b = false
  7. )
  8. type myInt int
  9. type myFloat float32
  10. type myString string
  11. func main() {
  12. var j myInt = a
  13. var f myFloat = pi
  14. var str myString = s
  15. fmt.Println(j) // 输出:5
  16. fmt.Println(f) // 输出:3.1415926
  17. fmt.Println(str) // 输出:Hello, Gopher
  18. }

无类型常量也拥有自己的默认类型:无类型的布尔型常量、无类型的整数常量、无类型的字符常量、无类型的浮点数常量、无类型的复数常量、无类型的字符串常量分别对应的默认类型为 bool、int、int32(rune)、float64、complex128 和 string。当常量被赋值给无类型变量、接口变量时,常量默认类型对于确定无类型变量的类型以及接口对应动态类型是至关重要的:

  1. const (
  2. a = 5
  3. s = "Hello, Gopher"
  4. )
  5. func main() {
  6. n := a
  7. var i interface{} = a
  8. fmt.Printf("%T\n", n) // 输出:int
  9. fmt.Printf("%T\n", i) // 输出:int
  10. i = s
  11. fmt.Printf("%T\n", i) // 输出:string
  12. }

4. 小结

数值型无类型常量还可以提供比基础类型更高精度的算术运算,你可以认为至少有 256bit 的运算精度。