package main

import (
	"fmt"
	"math"
	"time"
)

//type IP byte

// 常量表达式的值在编译期计算，而不是在运行期。
// 每种常量的潜在类型都是基础类型：boolean、string或数字。
func main() {
	const (
		PI = 3.14
		e  = 2.72
	)
	fmt.Println(math.Pi)
	fmt.Println(math.E)
	fmt.Println(PI)
	fmt.Println(e)

	// 所有常量的运算都可以在编译期完成，这样可以减少运行时的工作，
	// 也方便其他编译优化。当操作数是常量时，
	// 一些运行时的错误也可以在编译时被发现，
	// 例如整数除零、字符串索引越界、任何导致无效浮点数的操作等。

	// 常量间的所有算术运算、逻辑运算和比较运算的结果也是常量，
	// 对常量的类型转换操作或以下函数调用都是返回常量结果：
	// len、cap、real、imag、complex和unsafe.Sizeof（§13.1）。

	// 因为它们的值是在编译期就确定的，因此常量可以是构成类型的一部分，
	// 例如用于指定数组类型的长度：
	//const IPv4Len = 4
	// parseIPv4 parses an IPv4 address (d.d.d.d).
	//parseIPv4 := func(s string) IP {
	//	var p [IPv4Len]byte
	//	// ...
	//	return p
	//}
	// 一个常量的声明也可以包含一个类型和一个值，但是如果没有显式指明类型，
	// 那么将从右边的表达式推断类型。在下面的代码中，
	// time.Duration是一个命名类型，底层类型是int64，time.Minute是对应类型的常量。
	// 下面声明的两个常量都是time.Duration类型，可以通过%T参数打印类型信息：
	const noDelay time.Duration = 0
	const timeout = 5 * time.Minute
	fmt.Printf("%T %[1]v\n", noDelay)     // "time.Duration 0"
	fmt.Printf("%T %[1]v\n", timeout)     // "time.Duration 5m0s"
	fmt.Printf("%T %[1]v\n", time.Minute) // "time.Duration 1m0s"

	// 如果是批量声明的常量，除了第一个外其它的常量右边的初始化表达式都可以省略，
	// 如果省略初始化表达式则表示使用前面常量的初始化表达式写法，对应的常量类型也一样的。例如：
	const (
		a = 1
		b
		c = 2
		d
	)

	fmt.Println(a, b, c, d) // "1 1 2 2"

	// 下面是来自time包的例子，它首先定义了一个Weekday命名类型，
	// 然后为一周的每天定义了一个常量，从周日0开始。在其它编程语言中，
	// 这种类型一般被称为枚举类型。

	type Weekday int

	const (
		Sunday Weekday = iota
		Monday
		Tuesday
		Wednesday
		Thursday
		Friday
		Saturday
	)
	fmt.Printf("%T %[1]v \n", Sunday)

	// 下面是一个更复杂的例子，每个常量都是1024的幂：
	const (
		_   = 1 << (10 * iota)
		KiB // 1024
		MiB // 1048576
		GiB // 1073741824
		TiB // 1099511627776             (exceeds 1 << 32)
		PiB // 1125899906842624
		EiB // 1152921504606846976
		ZiB // 1180591620717411303424    (exceeds 1 << 64)
		YiB // 1208925819614629174706176
	)
	fmt.Println(KiB)
}
