Go has two allocation primitives, the built-in functions new and make. They do different things and apply to different types, which can be confusing, but the rules are simple. Let's talk about new first. It's a built-in function that allocates memory, but unlike its namesakes in some other languages it does not initialize the memory, it only zeros it. That is, new(T) allocates zeroed storage for a new item of type T and returns its address, a value of type *T. In Go terminology, it returns a pointer to a newly allocated zero value of type T. golang
Go有兩種分配內存的方式 new 和 make 這兩個函數所作的事情不同 而且對應了不一樣的類型 聽起來有點繞 可是它們的使用規則很簡單 先講new吧 new爲變量分配內存 和其它語言中的new關鍵字不同的是 它不會去對分配的內存作初始化 它僅僅用相應類型的零值賦給它 就是說 new(T)分配了一段已經賦爲零值的內存給新建立的T類型變量 而且返回它的地址指向類型T的零值 (這段推薦看原文) express
Since the memory returned by new is zeroed, it's helpful to arrange when designing your data structures that the zero value of each type can be used without further initialization. This means a user of the data structure can create one with new and get right to work. For example, the documentation for bytes.Buffer states that "the zero value for Buffer is an empty buffer ready to use." Similarly, sync.Mutex does not have an explicit constructor or Init method. Instead, the zero value for a sync.Mutex is defined to be an unlocked mutex. api
因爲分配的內存已經賦值爲相應類型的零值 在設計數據結構的時候合理地利用這個特性 會帶來額外的好處 你能夠不用對它進一步初始化 就可使用了 這意味着直接使用new建立的自定義數據類型 例如 bytes.Buffer的零值 就是一個已經可使用的空buffer 相似地 sync.Mutex沒有顯式的構造函數 也沒有Init初始化函數 而sync.Mutex的零值是沒有鎖上的互斥鎖 數組
The zero-value-is-useful property works transitively. Consider this type declaration. 數據結構
零值便可使用的特性對咱們來講是透明的 看下面這個例子 app
type SyncedBuffer struct { lock sync.Mutex buffer bytes.Buffer }
Values of type SyncedBuffer are also ready to use immediately upon allocation or just declaration. In the next snippet, both p and v will work correctly without further arrangement. less
SyncedBuffer在用new或者直接由var建立後就是使用了 如下的這段代碼中 p和v就是已經可使用了 不須要再額外地進行初始化 ide
p := new(SyncedBuffer) // type *SyncedBuffer var v SyncedBuffer // type SyncedBuffer
Sometimes the zero value isn't good enough and an initializing constructor is necessary, as in this example derived from package os. 函數
有的時候用new獲得的零值知足不了咱們的需求 咱們但願有初始化構造函數 就如同下面這個os包中NewFile函數: fetch
func NewFile(fd int, name string) *File { if fd < 0 { return nil } f := new(File) f.fd = fd f.name = name f.dirinfo = nil f.nepipe = 0 return f }
There's a lot of boiler plate in there. We can simplify it using a composite literal, which is an expression that creates a new instance each time it is evaluated.
能夠找到不少這樣的例子 不過咱們能夠簡化這樣的初始化語句 省的一句一句地寫 咱們可使用複合字面值 建立某個類型的實例:
func NewFile(fd int, name string) *File { if fd < 0 { return nil } f := File{fd, name, nil, 0} return &f }
Note that, unlike in C, it's perfectly OK to return the address of a local variable; the storage associated with the variable survives after the function returns. In fact, taking the address of a composite literal allocates a fresh instance each time it is evaluated, so we can combine these last two lines.
注意 這裏有一個和C不同的地方 咱們能夠返回一個局部變量的地址給函數的調用者 函數返回後 局部變量並無被銷燬 事實上 每次取複合字面值的地址(下面這個例子)都會從新建立一個新的File類型變量 因此上面那段代碼的最後兩句也能夠這樣寫:
return &File{fd, name, nil, 0}
The fields of a composite literal are laid out in order and must all be present. However, by labeling the elements explicitly as field:value pairs, the initializers can appear in any order, with the missing ones left as their respective zero values. Thus we could say
在使用複合字面值時 字面值的順序必須與該類型的定義一致 並且定義中的每一個字段都必需要有相應的字面值來初始化 可是 一旦明確地對標記出filed:value對時 每一個字段出現的順序能夠與定義中的順序不一樣 另外 使用這種方式的話 那些沒有明確寫出來的字段就是用該字段類型的零值來初始化 以下所示:
return &File{fd: fd, name: name}
As a limiting case, if a composite literal contains no fields at all, it creates a zero value for the type. The expressions new(File) and &File{} are equivalent.
若是複合字面值沒有包含任何字段 它會建立這個類型的零值 這個時候new(File) 和 &File{}是同樣的
Composite literals can also be created for arrays, slices, and maps, with the field labels being indices or map keys as appropriate. In these examples, the initializations work regardless of the values of Enone, Eio, and Einval, as long as they are distinct.
複合字面值也能夠用來建立數組 slice 或者map:
a := [...]string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"} // 原文有誤 s := []string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"} // 原文有誤 m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
Back to allocation. The built-in function make(T, args) serves a purpose different from new(T). It creates slices, maps, and channels only, and it returns an initialized (notzeroed) value of type T (not *T). The reason for the distinction is that these three types are, under the covers, references to data structures that must be initialized before use. A slice, for example, is a three-item descriptor containing a pointer to the data (inside an array), the length, and the capacity, and until those items are initialized, the slice is nil. For slices, maps, and channels, make initializes the internal data structure and prepares the value for use. For instance,
make和new的用途不同 make只能用來建立slice map和channel 它返回已經初始化過的T類型變量 須要單獨使用make來建立這三種類型的變量有特殊的緣由 這三種類型所引用的數據結構必須在初始化後才能使用 拿slice舉例來講吧 它是包括三個元素 分別是 指向底層數據結構(array)的指針 長度 以及容量 在對這些元素進行初始化以前slice的值爲nil make爲slice map和channel初始化其內部的數據結構 看下面這個例子:
make([]int, 10, 100)
allocates an array of 100 ints and then creates a slice structure with length 10 and a capacity of 100 pointing at the first 10 elements of the array. (When making a slice, the capacity can be omitted; see the section on slices for more information.) In contrast, new([]int) returns a pointer to a newly allocated, zeroed slice structure, that is, a pointer to a nil slice value.
上面這個例子分配了一個長度爲100的int數組 而後建立slice slice的長度爲10 容量爲100 這個新建立的slice指向底層數組的前10個元素 相較之下 new([]int)建立了一個新分配的slice 其值爲nil
These examples illustrate the difference between new and make.
下面幾個例子展現了new和make的差別:
var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful var v []int = make([]int, 100) // the slice v now refers to a new array of 100 ints // Unnecessarily complex: var p *[]int = new([]int) *p = make([]int, 100, 100) // Idiomatic: v := make([]int, 100)
Remember that make applies only to maps, slices and channels and does not return a pointer. To obtain an explicit pointer allocate with new.
記住:make只能使用在map slice和channel上 要獲取相應類型的指針 請使用new
Arrays are useful when planning the detailed layout of memory and sometimes can help avoid allocation, but primarily they are a building block for slices, the subject of the next section. To lay the foundation for that topic, here are a few words about arrays.
數組主要用來構建slice 下節咱們會講slice 爲了給下節作個鋪墊 這節就講講數組
There are major differences between the ways arrays work in Go and C. In Go,
Go與C中 數組的工做工做方式有很大區別:
The value property can be useful but also expensive; if you want C-like behavior and efficiency, you can pass a pointer to the array.
數組是值這個特色頗有用 可是代價也很高 若是你想像C同樣高效 你能夠給數組傳遞一個指針:
func Sum(a *[3]float64) (sum float64) { for _, v := range *a { sum += v } return } array := [...]float64{7.0, 8.5, 9.1} x := Sum(&array) // Note the explicit address-of operator
But even this style isn't idiomatic Go. Slices are. 但這個並非Go的習慣用法
Slices wrap arrays to give a more general, powerful, and convenient interface to sequences of data. Except for items with explicit dimension such as transformation matrices, most array programming in Go is done with slices rather than simple arrays.
slice對數組作了封裝 在使用數組這樣的數據時更加給力 更加易用 除非須要處理相似矩陣變換這樣的問題 大多數數組操做仍是由slice來完成的
Slices are reference types, which means that if you assign one slice to another, both refer to the same underlying array. For instance, if a function takes a slice argument, changes it makes to the elements of the slice will be visible to the caller, analogous to passing a pointer to the underlying array. A Read function can therefore accept a slice argument rather than a pointer and a count; the length within the slice sets an upper limit of how much data to read. Here is the signature of the Read method of theFile type in package os:
slice是引用類型 也就是說 若是你把一個slice賦值給另外一個slice 那麼它們底層所指向的數組是同一個 舉個例子 函數接受一個slice類型的參數 函數內對這個參數的操做 會直接影響到調用者 相似於C中 用指針做爲參數 Read函數 就能夠接受slice參數 而不是經過指針加元素個數做爲參數 slice的長度已經爲能夠讀多少數據設定了上限 下面這個是os包中File類型的Read方法的聲明:
func (file *File) Read(buf []byte) (n int, err error)
The method returns the number of bytes read and an error value, if any. To read into the first 32 bytes of a larger buffer b, slice (here used as a verb) the buffer.
它返回成功讀取的字節數 若是讀操做中出現了錯誤 那麼還會返回相應的錯誤值 讀去b中的頭32字節 能夠參考下面這個例子:
n, err := f.Read(buf[0:32])
Such slicing is common and efficient. In fact, leaving efficiency aside for the moment, the following snippet would also read the first 32 bytes of the buffer.
這樣的例子很常見 也很高效 事實上 拋開效率不說 下面這段代碼也能夠讀前32字節數據:
var n int var err error for i := 0; i < 32; i++ { nbytes, e := f.Read(buf[i:i+1]) // Read one byte. if nbytes == 0 || e != nil { err = e break } n += nbytes }
The length of a slice may be changed as long as it still fits within the limits of the underlying array; just assign it to a slice of itself. The capacity of a slice, accessible by the built-in function cap, reports the maximum length the slice may assume. Here is a function to append data to a slice. If the data exceeds the capacity, the slice is reallocated. The resulting slice is returned. The function uses the fact that len and cap are legal when applied to the nil slice, and return 0.
slice的長度是能夠伸縮的 只要它的長度沒有超過底層數組的容量 slice的容量 能夠經過函數cap來得到 這個函數返回slice的最大長度 下面這個函數給slice添加數據 若是超出了slice的容量 從新分配slice 並返回最終的slice 這個函數利用了len和cap函數 接收nil slice時 返回0
func Append(slice, data[]byte) []byte { l := len(slice) if l + len(data) > cap(slice) { // reallocate // Allocate double what's needed, for future growth. newSlice := make([]byte, (l+len(data))*2) // The copy function is predeclared and works for any slice type. copy(newSlice, slice) slice = newSlice } slice = slice[0:l+len(data)] for i, c := range data { slice[l+i] = c } return slice }
We must return the slice afterwards because, although Append can modify the elements of slice, the slice itself (the run-time data structure holding the pointer, length, and capacity) is passed by value.
咱們必須返回新建的slice 這是由於 雖然Append函數能夠直接修改slice 可是slice自己是按值傳遞的
The idea of appending to a slice is so useful it's captured by the append built-in function. To understand that function's design, though, we need a little more information, so we'll return to it later.
給slice添加數據是很是頻繁 有用的 Go中 能夠直接使用append內置函數來達到這個目的
Maps are a convenient and powerful built-in data structure to associate values of different types. The key can be of any type for which the equality operator is defined, such as integers, floating point and complex numbers, strings, pointers, interfaces (as long as the dynamic type supports equality), structs and arrays. Slices cannot be used as map keys, because equality is not defined on them. Like slices, maps are a reference type. If you pass a map to a function that changes the contents of the map, the changes will be visible in the caller.
map又是一個很是有用的數據類型 能夠把不一樣類型的數據聯繫到一塊兒 map中的key能夠是任何支持等於比較操做符的類型 例如int整數 浮點數 複數 字符串 指針 接口 結構體還有數組 slice不能用做map的key 由於slice並無定義等於比較操做符 和slice同樣 map也是引用類型
Maps can be constructed using the usual composite literal syntax with colon-separated key-value pairs, so it's easy to build them during initialization.
map可使用複合字面值 不一樣的key-value對的方式來初始化
var timeZone = map[string] int { "UTC": 0*60*60, "EST": -5*60*60, "CST": -6*60*60, "MST": -7*60*60, "PST": -8*60*60, }
Assigning and fetching map values looks syntactically just like doing the same for arrays and slices except that the index doesn't need to be an integer.
賦值和取map的值 語法上看起來和數組 slice相似 可是index並不須要是整數
offset := timeZone["EST"]
An attempt to fetch a map value with a key that is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0. A set can be implemented as a map with value type bool. Set the map entry to true to put the value in the set, and then test it by simple indexing.
用在map中不存在的key來取值時 會返回相應類型的零值 舉例來講 若是map包含整數類型 用不存在的key來取值時 會返回0 集合可使用bool類型的map來實現(值爲bool類型) 若是某個key的值爲true 那麼這個值在這個集合中 反之亦反
attended := map[string] bool { "Ann": true, "Joe": true, ... } if attended[person] { // will be false if person is not in the map fmt.Println(person, "was at the meeting") }
Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that zero value because it's not in the map at all? You can discriminate with a form of multiple assignment.
有的時候你可能須要明確地知道某個key 是否存在 而map的key不存在即返回相應類型的零值這個特性 並不能知足咱們的需求 map中不存在UTC這個key 仍是UTC這個key對應的值就是0? 咱們可使用下面的形式來區別這兩種狀況:
var seconds int var ok bool seconds, ok = timeZone[tz]
For obvious reasons this is called the 「comma ok」 idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false. Here's a function that puts it together with a nice error report:
很明顯啦 這個就是「comma ok」 這個例子中 若是tz存在 seconds就是正確的值 而ok是true 若是tz不存在 seconds的值爲0 ok就是false offset函數把這兩個值結合在一塊兒 給出一個比較友好的錯誤報告:
func offset(tz string) int { if seconds, ok := timeZone[tz]; ok { return seconds } log.Println("unknown time zone:", tz) return 0 }
To test for presence in the map without worrying about the actual value, you can use the blank identifier (_). The blank identifier can be assigned or declared with any value of any type, with the value discarded harmlessly. For testing just presence in a map, use the blank identifier in place of the usual variable for the value.
若是隻是想知道是否存在某個key 可使用_ (虛標識符?) 看下面這個例子:
_, present := timeZone[tz]
To delete a map entry, use the delete built-in function, whose arguments are the map and the key to be deleted. It's safe to do this this even if the key is already absent from the map.
可使用內置函數delete 刪除map中的某個key-value對 這個函數的參數是map 以及要被刪除的key 若是要刪除的key根本不存在 刪除操做也是ok的
delete(timeZone, "PDT") // Now on Standard Time
Formatted printing in Go uses a style similar to C's printf family but is richer and more general. The functions live in the fmt package and have capitalized names:fmt.Printf, fmt.Fprintf, fmt.Sprintf and so on. The string functions (Sprintf etc.) return a string rather than filling in a provided buffer.
Go中的格式化輸出和C的printf相似 可是功能更多 相關的格式化輸出函數在fmt包中 包括:fmt.Printf, fmt.Fprinf fmt.Sprintf等函數 string相關的函數 Sprintf等 返回一個字符串 而不是填充已有的buffer(參見c)
You don't need to provide a format string. For each of Printf, Fprintf and Sprintf there is another pair of functions, for instance Print and Println. These functions do not take a format string but instead generate a default format for each argument. The Println versions also insert a blank between arguments and append a newline to the output while the Print versions add blanks only if the operand on neither side is a string. In this example each line produces the same output.
對於每個Printf, Fprintf和Sprintf 都有一個對應的函數 好比Print和Println 這兩個函數並不須要傳入格式化字符串 可是它們會爲每一個參數提供默認的格式 Println函數同時會在各個參數間插入一個空白字符 而且在尾部添加一個換行符 而Print只有在相鄰的兩個參數都不是字符串時才添加空白字符 並且不會添加換行符:
fmt.Printf("Hello %d\n", 23) fmt.Fprint(os.Stdout, "Hello ", 23, "\n") fmt.Println("Hello", 23) fmt.Println(fmt.Sprint("Hello ", 23))
As mentioned in the Tour, fmt.Fprint and friends take as a first argument any object that implements the io.Writer interface; the variables os.Stdout and os.Stderr are familiar instances.
fmt.Fprint以及相關的函數 接受實現io.Writer接口的對象做爲其第一個參數 os.Stdout os.Stderr是常見的對象
Here things start to diverge from C. First, the numeric formats such as %d do not take flags for signedness or size; instead, the printing routines use the type of the argument to decide these properties.
和C不同的地方在於:首先 數值格式 好比%d並不像C那樣 接受正負或者長度標記 輸出函數使用它參數的類型來決定這些屬性
var x uint64 = 1<<64 - 1 fmt.Printf("%d %x; %d %x\n", x, x, int64(x), int64(x))
prints 輸出爲:
18446744073709551615 ffffffffffffffff; -1 -1
If you just want the default conversion, such as decimal for integers, you can use the catch all format %v (for 「value」); the result is exactly what Print and Println would produce. Moreover, that format can print any value, even arrays, slices, structs, and maps. Here is a print statement for the time zone map defined in the previous section.
若是隻須要默認的格式轉換 好比十進制整數 可使用%v 輸出的結果和Print, Println同樣 並且這個格式%v能夠以輸出任何值 甚至是數值 slice 結構體 和map 下面這段代碼輸出上面例子中定義的時區map
fmt.Printf("%v\n", timeZone) // or just fmt.Println(timeZone)
which gives output 上述代碼的輸出爲:
map[CST:-21600 PST:-28800 EST:-18000 UTC:0 MST:-25200]
For maps the keys may be output in any order, of course. When printing a struct, the modified format %+v annotates the fields of the structure with their names, and for any value the alternate format %#v prints the value in full Go syntax.
固然map的key輸出 多是亂序的 當輸出結構體是 %v修飾符 %+v會輸出結構體中的字段名 %#v輸出該變量的Go語法格式
type T struct { a int b float64 c string } t := &T{ 7, -2.35, "abc\tdef" } fmt.Printf("%v\n", t) fmt.Printf("%+v\n", t) fmt.Printf("%#v\n", t) fmt.Printf("%#v\n", timeZone)
prints
&{7 -2.35 abc def} &{a:7 b:-2.35 c:abc def} &main.T{a:7, b:-2.35, c:"abc\tdef"} map[string] int{"CST":-21600, "PST":-28800, "EST":-18000, "UTC":0, "MST":-25200}
(Note the ampersands注意&符號.) That quoted string format is also available through %q when applied to a value of type string or []byte; the alternate format %#q will use backquotes instead if possible. Also, %x works on strings, byte arrays and byte slices as well as on integers, generating a long hexadecimal string, and with a space in the format (% x) it puts spaces between the bytes.
%q在輸出字符串 或者 []byte時 會加雙引號 %#q則儘量使用反引號 另外 %x也能夠用在字符串 byte數組 byte slice 還有整數上 輸出十六進制字符串 若是在%和x之間有一個空格 則在輸出時會在不一樣的元素間添加空格
Another handy format is %T, which prints the type of a value.
還有一個很實用的形式是%T 它能夠輸出一個值的類型
fmt.Printf("%T\n", timeZone)
prints 輸出
map[string] int
If you want to control the default format for a custom type, all that's required is to define a method with the signature String() string on the type. For our simple type T, that might look like this.
若是你像格式化 自定義的類型 只須要在相應類型上定義一個String() string 方法 好比:
func (t *T) String() string { return fmt.Sprintf("%d/%g/%q", t.a, t.b, t.c) } fmt.Printf("%v\n", t)
to print in the format
輸出爲:
7/-2.35/"abc\tdef"
Our String method is able to call Sprintf because the print routines are fully reentrant and can be used recursively. We can even go one step further and pass a print routine's arguments directly to another such routine. The signature of Printf uses the type ...interface{} for its final argument to specify that an arbitrary number of parameters (of arbitrary type) can appear after the format.
咱們定義的String能夠調用Sprintf 由於輸出相關的函數是可重入的 而且能夠遞歸地調用 Printf函數的最後一個參數類型爲...interface{} 也就是說它在格式化字符串後 能夠接受任何數量任何類型的參數
func Printf(format string, v ...interface{}) (n int, err error) {
Within the function Printf, v acts like a variable of type []interface{} but if it is passed to another variadic function, it acts like a regular list of arguments. Here is the implementation of the function log.Println we used above. It passes its arguments directly to fmt.Sprintln for the actual formatting.
Printf函數中 v是[]interface{}類型的變量 可是當把它傳遞給另外一個接受可變參數的函數時 它表現的和正常的參數列表同樣 以下面這個例子:
// Println prints to the standard logger in the manner of fmt.Println. func Println(v ...interface{}) { std.Output(2, fmt.Sprintln(v...)) // Output takes parameters (int, string) }
We write ... after v in the nested call to Sprintln to tell the compiler to treat v as a list of arguments; otherwise it would just pass v as a single slice argument.
v後面的...告訴編譯器 把v當成是參數列表 不然編譯器會把v當成是單個slice參數(單個參數 而不是多個參數組成的列表)
There's even more to printing than we've covered here. See the godoc documentation for package fmt for the details.
關於輸出的更多內容 請參考godoc中fmt包的文檔
By the way, a ... parameter can be of a specific type, for instance ...int for a min function that chooses the least of a list of integers:
...參數能夠用於特定類型的參數 好比 min函數的...int參數 傳遞了多個整數給min函數
func Min(a ...int) int { min := int(^uint(0) >> 1) // largest int for _, i := range a { if i < min { min = i } } return min }
Now we have the missing piece we needed to explain the design of the append built-in function. The signature of append is different from our custom Append function above. Schematically, it's like this:
如今 咱們來介紹一下內建函數append 下面是append函數的聲明:
func append(slice []T, elements...T) []T
where T is a placeholder for any given type. You can't actually write a function in Go where the type T is determined by the caller. That's why append is built in: it needs support from the compiler.
這裏的T能夠是任意的類型 在Go中 你不可能寫個函數 它的參數類型由調用者來決定 這也就是爲何append是內置函數 它須要編譯器的幫助
What append does is append the elements to the end of the slice and return the result. The result needs to be returned because, as with our hand-written Append, the underlying array may change. This simple example
append會在slice的末尾添加一個元素 而且返回結果 最終的slice須要被返回 是由於slice對應的底層數組可能已經改變了 看幾個簡單的例子:
x := []int{1,2,3} x = append(x, 4, 5, 6) fmt.Println(x)
prints [1 2 3 4 5 6]. So append works a little like Printf, collecting an arbitrary number of arguments.
append和Printf相似 均可以接受任意數量的參數
But what if we wanted to do what our Append does and append a slice to a slice? Easy: use ... at the call site, just as we did in the call to Output above. This snippet produces identical output to the one above.
若是咱們想給slice添加一個slice該怎麼作呢 很簡單 可使用... 以下所示:
x := []int{1,2,3} y := []int{4,5,6} x = append(x, y...) fmt.Println(x)
Without that ..., it wouldn't compile because the types would be wrong; y is not of type int.
沒有...的話 編譯通不過 由於y的類型不是整數int