5.9. 初始化

尽管表面看来和 C 或 C++ 的初始化没什么不同,Go 的更够强。复杂结构可在初始化时架设,并且不同包的物件的初始化顺序问题也得到正确处理。

5.9.1. Constants 常量初始化

常量在 Go 里是 —— 不变的。它们在编译时生成,即便是局部定义在函数里。它只能是数,字串或布尔。因为编译态的限制,定义它们的表达式必须是常量表达式,可以被编译器求值。例如,1<<3 是常量表达式, math.Sin(math.Pi/4) 不是,因为 math.Sin 的函数调用发生在运行态。

Go 的列举常量可用 iota 生成。 因为 iota 可以是表达式的一部分,并且表达式可以隐含重复,打造一套精致的值可以变得很容易。

  type ByteSize float64
  const (
      _ = iota  // ignore first value by assigning to blank identifier
      KB ByteSize = 1<<(10*iota)
      MB
      GB
      TB
      PB
      EB
      ZB
      YB
  )

给类型添加比如 String 等方法的本领,使值自动排版打印自己变得可能,即使只作为通用类型的一部分。

  func (b ByteSize) String() string {
      switch {
      case b >= YB:
          return fmt.Sprintf("%.2fYB", b/YB)
      case b >= ZB:
          return fmt.Sprintf("%.2fZB", b/ZB)
      case b >= EB:
          return fmt.Sprintf("%.2fEB", b/EB)
      case b >= PB:
          return fmt.Sprintf("%.2fPB", b/PB)
      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("%.2fB", b)
  }

表达式 YB 打印 1.00YB,而 ByteSize(1e13) 打印 9.09TB

5.9.2. 变量初始化

变量和常量的初始化一样,但可以用运行态计算的普通表达式。

  var (
      HOME = os.Getenv("HOME")
      USER = os.Getenv("USER")
      GOROOT = os.Getenv("GOROOT")
  )

5.9.3. init函数

最后,每个源文件可以定义自身的 init() 函数来安排所需的状态。唯一的限制是,尽管够程可在初始化时启动,它们只在初始完成后执行;初始化永远是单一的执行序列。最后之后,init() 发生在包里所有变量初始化之后,而其又发生在所有的包全部导入之后。

除了初始化不能表示为声明外,init() 函数常用来在程序运行前验证或修补其状态。

  func init() {
      if USER == "" {
          log.Exit("$USER not set")
      }
      if HOME == "" {
          HOME = "/usr/" + USER
      }
      if GOROOT == "" {
          GOROOT = HOME + "/go"
      }
      // GOROOT may be overridden by --goroot flag on command line.
      flag.StringVar(&GOROOT, "goroot", GOROOT, "Go root directory")
  }