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泄漏问题。