天天看點

《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

第10條 使用iota實作枚舉常量

Go的const文法提供了“隐式重複前一個非空表達式”的機制,來看下面的代碼:
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

常量定義的後兩行沒有顯式給予初始指派,Go 編譯器将為其隐式使用第一行的表達式,這樣上述定義等價于:

《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記
iota是Go語言的一個預定義辨別符,它表示的是const聲明塊(包括單行聲明)中每個常量所處位置在塊中的偏移值(從零開始)。

iota的值和在const塊中第幾行有關,并不是在哪第一次使用都是0

位于同一行的iota即便出現多次,其值也是一樣的:
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記
如果要略過iota = 0,而從iota = 1開始正式定義枚舉常量,可以效仿下面的代碼:
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

iota雖然是第一次使用,但它在const塊的第二行,是以值為1,而不是0

舉一個“反例”:在一些枚舉常量名稱與其初始值有強烈對應關系的時候,枚舉常量會直接使用顯式數值作為常量的初始值。這樣的情況極其少見,我在Go标準庫中僅找到這一處:
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

一般使用iota

第11條 盡量定義零值可用的類型

11.2 零值可用

Go從誕生以來就一直秉承着盡量保持“零值可用”的理念
切片零值可用
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記
string方法零值可用
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記
鎖零值可用
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記
bytes.Buffer零值可用
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

這是因為bytes.Buffer結構體用于存儲資料的字段buf支援零值可用政策的切片類型

《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記
Go語言零值可用的理念給内置類型、标準庫的使用者帶來很多便利。不過Go并非所有類型都是零值可用的,并且零值可用也有一定的限制,比如:在append場景下,零值可用的切片類型不能通過下标形式操作資料:
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記
另外,像map這樣的原生類型也沒有提供對零值可用的支援
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記
零值可用的類型要注意盡量避免值複制:
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

我們可以通過指針方式傳遞類似 Mutex 這樣的類型:

《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

這點不了解,這個零值就是個nil,為啥不能指派給其他變量呢?

一番測試後,搞明白了

type Person struct {
    age     int
    address string
}

func main() {
    var p Person
    fmt.Printf("p:%v\n", p)         // p:{0 }
    fmt.Printf("p:%p\n", &p)        // p:0xc0000ac018
    fmt.Printf("p.age:%v\n", p.age) // p.age:0

    var p1 *Person
    fmt.Printf("p1:%v\n", p1)         // p1:<nil>
    fmt.Printf("p1:%p\n", &p1)        // p1:0xc0000b4020
    fmt.Printf("p1.age:%v\n", p1.age) // 報錯
}           

複制

未指派的結構體變量是配置設定了記憶體的,不等于nil,并且結構體内的字段都賦予了初始值。

未指派的指針等于nil,未配置設定記憶體。

這就能解釋為什麼mutex未指派就能調用自己的Lock方法而不會NPE。

var lock sync.Mutex
lock.Lock()           

複制

保持與Go一緻的理念,給自定義的類型一個合理的零值,并盡量保持自定義類型的零值可用,這樣我們的Go代碼會更加符合Go語言的慣用法。

第12條 使用複合字面值作為初值構造器

《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

12.1 結構體複合字面值

一旦該結構體類型增加了一個新的字段,即使是未導出的,這種值構造方式也将導緻編譯失敗,也就是說,應該将
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

替換為

《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

顯然,Go推薦使用field:value的複合字面值形式對struct類型變量進行值構造,這種值構造方式可以降低結構體類型使用者與結構體類型設計者之間的耦合。

可讀性更好、增加字段不會編譯錯誤(解耦)、無順序要求、不容易出錯

複合字面值作為結構體值構造器的大量使用,使得即便采用類型零值時我們也會使用字面值構造器形式:

s := myStruct{} // 常用

而較少使用new這一個Go預定義的函數來建立結構體變量執行個體:

s := new(myStruct) // 較少使用

12.3 map複合字面值

對于數組/切片類型而言,當元素為複合類型時,可以省去元素複合字面量中的類型,比如:
《Go語言精進之路:從新手到高手的程式設計思想、方法和技巧1》10-12章筆記

還有map

對于key或value為指針類型的情況,也可以省略“&T”

對于零值不适用的場景,我們要為變量賦予一定的初值。對于複合類型,我們應該首選Go提供的複合字面值作為初值構造器。對于不同複合類型,我們要記住下面幾點:

對于零值不适用的場景,我們要為變量賦予一定的初值。對于複合類型,我們應該首選Go提供的複合字面值作為初值構造器。對于不同複合類型,我們要記住下面幾點:

1、使用field:value形式的複合字面值為結構體類型的變量賦初值;

2、在為稀疏元素指派或讓編譯器推導數組大小的時候,多使用index:value的形式為數組/切片類型變量賦初值;

3、使用key:value形式的複合字面值為map類型的變量賦初值。(Go1.5版本後,複合字面值中的key和value類型均可以省略不寫。)

Post Views: 8