1、如果s是非ASCII编码的字符,建议使用以下函数来对字符进行定位:

strings.IndexRune(s string, r rune) int

2、高效统计unicode字符串内字符数:utf8.RuneCountInString(s)。

3、拼接字符串更好的办法是使用函数strings.Join(),甚至使用字节缓冲bytes.Buffer。

4、通过函数len()来获取到的是字符串的字节长度,如果要获取非ASCII编码字符串的字符数量需要len([]rune(str))。

5、几乎所有全局作用域的类型、常量、变量、函数和被导出的对象都应该有一个合理的注释。如果这种注释(称为文档注释)出现在函数前面,例如函数 Abcd,则要以 "Abcd..." 作为开头。

6、在fmt.Printf中使用%T打印某个类型的完整说明。

7、浮点数除以0.0会返回一个无穷尽的结果,使用+Inf表示,并不会报错。

8、string大致的内部实现为:

type string {
    *[]byte str
    int len
}

函数中string类型的参数传递并不会引发性能问题。

9、os.Exit(1)会马上退出进程,defer函数全部不会执行。

10、返回值可以被命名,如果有return则被覆盖,接下来如果defer则可能被defer修改。

11、append本质是复制操作,如果空间不够,则会新申请一块内存,并把原块中的值复制过去,与多线程交互的时候应该特别注意。

12、切片是引用类型,[]type,与数组不同的是它不会确定长度,切片不能被重新分片以获取数组的前一个元素 切片只能向后移动。用法:

s1 := s1[1:]

容量是底级数组的最大索引与取出元素的最小索引的差值。当切片的容量发生变量时,会重新分配地址,所以此时切片和原底级数组的关联就会断开,修改不再影响对方,这一点要注意。

13、切片常用操作

1)将切片 b 的元素追加到切片 a 之后:

a = append(a, b...)

2)复制切片 a 的元素到新的切片 b 上:

b = make([]T, len(a))
copy(b, a)

3)删除位于索引 i 的元素:

a = append(a[:i], a[i+1:]...)

4)切除切片 a 中从索引 i 至 j 位置的元素:

a = append(a[:i], a[j:]...)

5)为切片 a 扩展 j 个元素长度:

a = append(a, make([]T, j)...)

6)在索引 i 的位置插入元素 x:

a = append(a[:i], append([]T{x}, a[i:]...)...)

7)在索引 i 的位置插入长度为 j 的新切片:

a = append(a[:i], append(make([]T, j), a[i:]...)...)

8)在索引 i 的位置插入切片 b 的所有元素:

a = append(a[:i], append(b, a[i:]...)...)

9)取出位于切片 a 最末尾的元素 x:

x, a = a[len(a)-1], a[:len(a)-1]

10)将元素 x 追加到切片 a:

a = append(a, x)

14、 如果想知道结构体类型T的一个实例占用了多少内存,可以使用:size := unsafe.Sizeof(T{})。

15、 打印内存值,黑魔法,不要在生产环境使用。

fmt.Println(*(*[unsafe.Sizeof(v)]byte)(unsafe.Pointer(&v)))

16、这是个无限递归:

func (t TT) String() string {
    return fmt.Sprintf("%v", t)
}

fmt内会默认调用类型的String方法,造成无限递归。

17、json.Marshal()是会解析指针的,所以可以递归json结构的解析,但是当一个结构体包含一个指向自己的指针时,会无限递归。

18、通过调用 runtime.GC() 函数可以显式的触发GC。

19、通过调用 runtime.Gosched() 会让出处理器。

20、打印调用栈:

log.Printf("packagename: error: %vn%s", err, debug.Stack())

21、批量类型断言type-switch,不允许有 fallthrough。

func classifier(items ...interface{}) {
    for i, x := range items {
        switch x.(type) {
        case bool:
            fmt.Printf("Param #%d is a booln", i)
        case float64:
            fmt.Printf("Param #%d is a float64n", i)
        case int, int64:
            fmt.Printf("Param #%d is a intn", i)
        case nil:
            fmt.Printf("Param #%d is a niln", i)
        case string:
            fmt.Printf("Param #%d is a stringn", i)
        default:
            fmt.Printf("Param #%d is unknownn", i)
        }
    }
}

22、每个 interface {} 变量在内存中占据两个字长:一个用来存储它包含的类型,另一个用来存储它包含的数据或者指向数据的指针。

23、对一个已经被close过的channel执行接收操作依然可以接受到之前已经成功发送的数据;如果channel中已经没有数据的话将产生一个零值的数据。

24、golang context应该一直使用WithCancel 或者WithTimeout,并保证defer cancel(),不然有可能出现goroutine泄漏问题。