再看Effective Go

  1. 若有名为 owner (小写,未导出)的字段,其获取器应当名为 Owner(大写,可导出)而非 GetOwner。大写字母即为可导出的这种规定为区分方法和字段提供了便利。 若要提供设置器方法,SetOwner 是个不错的选择
  2. 按照约定,只包含一个方法的接口应当以该方法的名称加上-er后缀来命名,如 Reader、Writer。
  3. Go中约定使用驼峰记法 MixedCaps 或 mixedCaps
  4. Go的正式语法使用分号来结束语句;和C不同的是,这些分号并不在源码中出现。所以Go并不是没有分号,只是和JS一样会自动添加分号。
  5. 对于字符串,range 能够提供更多便利。它能通过解析UTF-8, 将每个独立的Unicode码点分离出来。错误的编码(非UTF8编码)将占用一个字节,并以符文U+FFFD来代替。
  6. switch 后面没有表达式,它将匹配值为 true 的case,因此,我们可以将 if-else-if-else 链写成一个 switch
  7. 被推迟函数的实参(如果该函数为方法则还包括接收者)在推迟执行时就会求值,而不是在调用执行时才求值。这样无需担心变量值在函数执行时被改变。// 记得JS中setInterval的一个例子吗?
  8. new(T) 会为类型为 T 的新项分配已置零的内存空间, 并返回它的地址,也就是一个类型为 *T 的值
  9. 内建函数 make(T, args) 的目的不同于 new(T)。它只用于创建切片、映射和信道,并返回类型为 T(而非 *T)的一个已初始化 (而非置零)的值。
  10. 数组是值类型,不是引用,将一个数组赋予另一个数组会复制其所有元素,且数组的大小是其类型的一部分。不指定长度的数组字面量: a := [...]string{"a", "b", "c“}
  11. 切片保存了对底层数组的引用,将一个切片赋予另一个切片,它们会引用同一个数组。
  12. 映射的键可以是任何定义了相等性操作的类型,如整数,浮点数,复数,字符串,指针,接口,结构以及数组,但是不能是切片,因为切片的相等性还没有定义。
  13. 不要通过Sprintf来构造String方法,因为它会无限递归String方法
  14. 每个源文件都可以通过定义自己的无参数init函数来设置一些必要的状态,而它的结束就意味着初始化结束。
  15. init函数除了初始化那些不能被表示成声明的初始化外,init函数还常被用在程序真正开始执行前,检验或校正程序的状态。
  16. 类型断言: value.(type),称为类型选择,这里的type是一个关键字。类型断言接受一个接口值,并从中提取指定的明确类型的值,其语法借鉴自类型选择开头的子句,但他需要一个明确的类型,而非type关键字,如 str, ok := value.(string)
  17. 若某种现有的类型仅实现了一个接口,且除此之外并无可导出的方法,则该类型本身就无需导出,仅导出接口就能让我们更专注于其行为而非实现,其它属性不同的实现则能镜像该原始类型的行为。
  18. 我们可以为除指针和接口以外的任何类型定义方法,同样也能为一个函数写一个方法。并且为函数定义的方法可以反过来调用函数本身。
  19. 为忽略错误的返回而使用空白标识符是一中糟糕的实践。
  20. 若某个包或者声明变量没有使用就会产生错误。要让编译器停止关于使用导入的抱怨,需要使用空白标识符来引用已导入包中的符号。
  21. 使用空白标识符声明一个变量来确保某个变量一定实现了某个接口,一个示例: var _ json.Marshaler = (*RawMessage)(nil),调用一个 *RawMessage 转换并将其赋予Marshaler来要求RawMessage实现了Marshaler。这里的声明只是为了检查,而没有任何其他意义。这中声明是很少用的。
  22. 当内嵌一个类型时,该类型的方法会成为外部类型的方法, 但当它们被调用时,该方法的接收者是内部类型,而非外部的
  23. 内嵌类型会引入命名冲突的问题,但解决规则却很简单。
    1. 首先,字段或方法 X 会隐藏该类型中更深层嵌套的其它项 X。若 log.Logger 包含一个名为 Command 的字段或方法,Job 的 Command 字段会覆盖它。
    2. 其次,若相同的嵌套层级上出现同名冲突,通常会产生一个错误。若 Job 结构体中包含名为 Logger 的字段或方法,再将 log.Logger 内嵌到其中的话就会产生错误。然而,若重名永远不会在该类型定义之外的程序中使用,那就不会出错
  24. 不要通过共享内存来通信,而应通过通信来共享内存。
  25. Go将共享的值通过信道传递,多个多利执行的线程从不会主动共享,在一个时间点,只有一个Go程能访问该值。
  26. Go程常和函数字面量结合使用。在Go中,函数字面量都是闭包,其实现在保证了函数内引用变量的生命周期与函数的活动时间相同。
  27. 信道使用make分配内存,默认是不带缓冲区的或者同步的信道,通过可选的整形参数设置缓冲区大小。
  28. 为了防止循环内创建的Go程共享了变量,可以用相同的名字获得该变量的一个新的版本,以此来局部地刻意屏蔽循环变量,使它对每个Go程保持唯一。
  29. 服务器处理请求中,一种管理资源的好方法就是启动固定数量的 handle Go程,一起从请求信道中读取数据,这样可以更好地复用Go程。而不是从请求信道读取数据后创建Go程去处理它,虽然Go程的开销很小,能省则省。
  30. 信道是一等值。可以像其他值一样到处传递。 // 信道中的信道
  31. 并发是用可独立执行的组件构造程序的方法, 而并行则是为了效率在多CPU上平行地进行计算。
  32. 尽管Go的并发特性能够让某些问题更易构造成并行计算,但Go仍然并发而非并行的语言,且Go的模型并不适合所有的并行问题。
  33. panic()recover(), 在函数调用了panic后,立即停止当前函数的执行,开始回溯Go程的栈,运行任何被推迟的函数。所以 recover() 用在panic回溯路径上函数的defer函数中。
  34. 直接从defer函数中调用recover时不会返回nil,因此被推迟的代码能够调用本身使用了 panic 和 recover 的库函数而不会失败。