目录 [−]
原文: Golang Summaries by TapirLiu
本文是由TapirLiu总结的Golang中的一些知识点,对于深入学习Golang很有帮助,所以我特意翻译了一下。
各种类型复制的时候的花费
本节标题也可以叫做“各种类型的值的大小” (the sizes of values of all kinds of types),底层可被不同的值共享的数据的大小未被计算。
下面的表格中一个 word在32bit操作系统中代表4个字节,在64bit操作系统中代表8个字节,内容基于官方的Go 1.7的编译器。
| Type | Cost Of Value Copying (Value Size) | 
|---|---|
| bool | 1 byte | 
| int8, uint8, byte | 1 byte | 
| int16, uint16 | 2 bytes | 
| int32, uint32, rune | 4 bytes | 
| int64, uint64 | 8 bytes | 
| int, uint, uintptr | 1 word | 
| string | 2 words | 
| pointer | 1 word | 
| slice | 3 words | 
| map | 1 word | 
| channel | 1 word | 
| function | 1 word | 
| interface | 2 words | 
| struct | the sum of sizes of all fields | 
| array | (element value size) * (array length) | 
可使用内建函数的类型 (len、cap、close、delete、make)
| len | cap | close | delete | make | |
|---|---|---|---|---|---|
| string | Yes | ||||
| array (and array pointer) | Yes | Yes | |||
| slice | Yes | Yes | Yes | ||
| map | Yes | Yes | Yes | ||
| channel | Yes | Yes | Yes | Yes | 
上面的所有类型都可以使用 range遍历。
可以用作len函数参数的类型也叫做容器类型。
内建容器类型的值比较
假定容器的值可寻址(addressable)。
| Type | 是否可以增加新元素 | 元素可更新 | 元素可寻址 | 查找会更改容器的长度 | 底层元素可以共享 | 
|---|---|---|---|---|---|
| string | No | No | No | No | Yes(1) | 
| array | No | Yes(2) | Yes(2) | No | No | 
| slice | No(3) | Yes | Yes | No | Yes | 
| map | Yes | Yes | No | No | Yes | 
| channel | Yes(4) | No | No | Yes | Yes | 
(1) 针对官方的实现
(2) 针对可寻址的数组
(3) slice的长度可以使用reflect.SetLen修改。Increase the length of a slice by this way is kind of adding new elements into the slice.
(4) 只针对未满的缓存channel.
组合类型T{...}的值比较
Type (T) | 
T{}是类型T的零值? | 
|---|---|
| struct | Yes | 
| array | Yes | 
| slice | No (零值是 nil) | 
| map | No (零值是 nil) | 
零值是nil的类型
| Type (T) | Size Of T(nil) | 
|---|---|
| pointer | 1 word | 
| slice | 3 words | 
| map | 1 word | 
| channel | 1 word | 
| function | 1 word | 
| interface | 2 words | 
这些类型的零值的大小和上面类型的大小保持一致。
编译时被执行的函数
如果函数在编译时被执行,那么它的返回值是常量。
| Function | 返回值 | 编译时便计算? | 
|---|---|---|
| unsafe.Sizeof | uintptr |     Yes, 总是 | 
| unsafe.Alignof | ||
| unsafe.Offsetof | ||
| len | int |             有时候是                 Go 规范中讲到:              
  |     
| cap | ||
| real | float64(默认类型)  |             有时候是         |     
| imag | ||
| complex | complex128(默认类型)  |             有时候是         |     
不能被寻址的值
下面的值不能被寻址(addresses):
- bytes in strings:字符串中的字节
 - map elements:map中的元素
 - dynamic values of interface values (exposed by type assertions):接口的动态值
 - constant values:常量
 - literal values:字面值
 - package level functions:包级别的函数
 - methods (used as function values):方法
 - intermediate values:中间值
- function callings
 - explicit value conversions
 - all sorts of operations, except pointer dereference operations, but including:
- channel receive operations
 - sub-string operations
 - sub-slice operations
 - addition, subtraction, multiplication, and division, etc.
 
 
 
注意, &T{}相当于tmp := T{}; (&tmp)的语法糖,所以&T{}可合法不意味着T{}可寻址。
下面的值可以寻址:
- variables
 - fields of addressable structs
 - elements of addressable arrays
 - elements of any slices (whether the slices are addressable or not)
 - pointer dereference operations
 
不支持比较的类型
下面的类型不支持直接比较:
- map
 - slice
 - function
 - struct types containing incomparable fields
 - array types with incomparable elements
 
这些不能直接比较的类型不能用做map的key值。
注意:尽管map、slice、function类型不支持直接比较,但是它们的值却可以和nil直接比较。如果两个接口的动态类型不能比较,运行时比较这两个接口会panic,除非其中一个的动态值是untyped nil。
可命名的源代码元素
下面的源代码元素可以命名,名称必须是 Identifier
可以使用 _ 做名称? | 
|
|---|---|
| package | No | 
| import | Yes | 
| type | Yes | 
| variable | Yes | 
| constant | Yes | 
| function | Yes | 
| label | Yes | 
命名的源代码元素可以使用 ()分组声明
下面的类型可以使用()分组生命
- import
 - type
 - variable
 - constant
 
函数和标签(label)不能使用()分组声明。
可以在函数内外声明的源代码元素
下面的类型可以声明在函数内,也可以声明在函数外:
- type
 - variable
 - constant
 
import必须在其它元素的声明的前面(package语句的后面)。
函数在其它函数的外面声明。(译者注:函数变量/匿名函数可以在函数内声明)
标签(label)必须声明在函数内。
可以返回一个可选bool返回值的表达式
下面的表达式可以返回一个可选的bool值:
| 可选的bool返回值的意义 | 忽略可选值会影响程序的行为? | |
|---|---|---|
| map element access | map中是否包含要 | No | 
| channel value receive | 在channel关闭前收到的值是否已发出 | No | 
| type assertion | 接口的动态类型是否符合asserted type | Yes | 
(当可选值被忽略时,如果类型不match则会抛出panic)|
使用channel机制永远阻塞当前goroutine的方法
下面的方法都可以永远阻塞当前的goroutine:
1、receive from a channel which no values will be sent to
  | 
  | 
2、send value to a channel which no ones will receive values from
  | 
  | 
3、receive value from a nil channel
  | 
  | 
4、send value to a nil channel
  | 
  | 
5、use a bare select block
  | 
  | 
连接字符串的几种方法
下面几种方法都可以连接字符串(译者注:不考虑性能):
1、使用+连接字符串。如果连接的字符串少于6个,官方的编译器会对此优化,所以通常使用+简便而有效。
2、使用strings包中的strings.Join连接字符串。
3、使用fmt包中的fmt.Sprintf, fmt.Sprint 和 fmt.Sprintln连接字符串。这三个方法可以将任意的类型连接成字符串。fmt.Sprintln会在字符串之间加空格,在字符串尾部加新的换行符。如果两个值中的至少一个不是字符串,fmt.Sprint会在它们之间加空格。
4、包bytes的Buffer类型(或者内建函数copy)可以用来构建 byte slice, byte slice可以方便地转换成字符串。
