《Go入門指南》一些筆記

《Go入門指南》筆記

Map 刪除:delete(map1, "Washington")

假設(shè)我們想獲取一個(gè) map 類型的切片装畅,我們必須使用兩次 make() 函數(shù),第一次分配切片盒齿,第二次分配 切片中每個(gè) map 元素

container:list/ring

runtime

  • reflect: 實(shí)現(xiàn)通過程序運(yùn)行時(shí)反射,讓程序操作任意類型的變量。
  • runtime: Go 程序運(yùn)行時(shí)的交互操作接谨,例如垃圾回收和協(xié)程創(chuàng)建。
  • regex: 正則
  • sync.Mutex:加鎖
  • godoc -http=:6060 -goroot="." 可以查看文檔

如何強(qiáng)制使用工廠方法塘匣?

Go 語言不支持面向?qū)ο缶幊陶Z言中那樣的構(gòu)造子方法脓豪,但是可以很容易的在 Go 中實(shí)現(xiàn) “構(gòu)造子工廠” 方法。為了方便通常會(huì)為類型定義一個(gè)工廠忌卤,按慣例扫夜,工廠的名字以 new 或 New 開頭。

將對(duì)象聲明稱私有(即小寫開頭)驰徊,調(diào)用Newxx()方法笤闯。

// 注意為私有方法
type matrix struct {
    ...
}
func NewMatrix(params) *matrix {
    m := new(matrix) // 初始化 m
    return m
}

匿名字段和內(nèi)嵌結(jié)構(gòu)體?

當(dāng)兩個(gè)字段擁有相同的名字(可能是繼承來的名字)時(shí)該怎么辦呢棍厂?

  1. 外層名字會(huì)覆蓋內(nèi)層名字(但是兩者的內(nèi)存空間都保留)颗味,這提供了一種重載字段或方法的方式;
  2. 如果相同的名字在同一級(jí)別出現(xiàn)了兩次牺弹,如果這個(gè)名字被程序使用了浦马,將會(huì)引發(fā)一個(gè)錯(cuò)誤(不使用沒關(guān)系)时呀。沒有辦法來解決這種問題引起的二義性,必須由程序員自己修正晶默。

在一個(gè)對(duì)象 obj 被從內(nèi)存移除前執(zhí)行一些特殊操作退唠?

如果需要在一個(gè)對(duì)象 obj 被從內(nèi)存移除前執(zhí)行一些特殊操作,比如寫到日志文件中荤胁,可以通過如下方式調(diào)用函數(shù)來實(shí)現(xiàn):

runtime.SetFinalizer(obj, func(obj *typeObj))

func(obj *typeObj) 需要一個(gè) typeObj 類型的指針參數(shù) obj瞧预,特殊操作會(huì)在它上面執(zhí)行。func 也可以是一個(gè)匿名函數(shù)仅政。

在對(duì)象被 GC 進(jìn)程選中并從內(nèi)存中移除以前垢油,SetFinalizer 都不會(huì)執(zhí)行,即使程序正常結(jié)束或者發(fā)生錯(cuò)誤

接口命名約定圆丹?

(按照約定滩愁,只包含一個(gè)方法的)接口的名字由方法名加 [e]r 后綴組成,例如 Printer辫封、Reader硝枉、WriterLogger倦微、Converter 等等妻味。還有一些不常用的方式(當(dāng)后綴 er不合適時(shí)),比如 Recoverable欣福,此時(shí)接口名以 able 結(jié)尾责球,或者以 I 開頭(像 .NETJava 中那樣)

一個(gè)接口可以包含一個(gè)或多個(gè)其他的接口,這相當(dāng)于直接將這些內(nèi)嵌接口的方法列舉在外層接口中一樣拓劝。

接口類型判斷?

Type-switch

func classifier(items ...interface{}) {
    for i, x := range items {
        switch x.(type) {
        case bool:
            fmt.Printf("Param #%d is a bool\n", i)
        case float64:
            fmt.Printf("Param #%d is a float64\n", i)
        case int, int64:
            fmt.Printf("Param #%d is a int\n", i)
        case nil:
            fmt.Printf("Param #%d is a nil\n", i)
        case string:
            fmt.Printf("Param #%d is a string\n", i)
        default:
            fmt.Printf("Param #%d is unknown\n", i)
        }
    }
}

如何使用接口來獲取通用型雏逾?

func Sort(data Sorter) {
    for pass := 1; pass < data.Len(); pass++ {
        for i := 0;i < data.Len() - pass; i++ {
            if data.Less(i+1, i) {
                data.Swap(i, i + 1)
            }
        }
    }
}
type Sorter interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

現(xiàn)在如果我們想對(duì)一個(gè) int 數(shù)組進(jìn)行排序,所有必須做的事情就是:為數(shù)組定一個(gè)類型并在它上面實(shí)現(xiàn) Sorter接口的方法:

type IntArray []int
func (p IntArray) Len() int           { return len(p) }
func (p IntArray) Less(i, j int) bool { return p[i] < p[j] }
func (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
a := sort.IntArray(data) //conversion to type IntArray from package sort
sort.Sort(a)

切片注意事項(xiàng)郑临?

復(fù)制數(shù)據(jù)切片至空接口切片栖博,必須要顯式復(fù)制

切片的底層指向一個(gè)數(shù)組,該數(shù)組的實(shí)際容量可能要大于切片所定義的容量厢洞。只有在沒有任何切片指向的時(shí)候仇让,底層的數(shù)組內(nèi)存才會(huì)被釋放,這種特性有時(shí)會(huì)導(dǎo)致程序占用多余的內(nèi)存犀变。

Map注意事項(xiàng)妹孙?

Map使用make進(jìn)行初始化 不要使用new;假設(shè)我們想獲取一個(gè) map 類型的切片获枝,我們必須使用兩次 make() 函數(shù)蠢正,第一次分配切片,第二次分配 切片中每個(gè) map 元素

判斷key是否存在 : val1, isPresent = map1[key1]

map中刪除key: delete(map1, key1)

Map排序:如果你想為 map 排序省店,需要將 key(或者 value)拷貝到一個(gè)切片嚣崭,再對(duì)切片排序(使用 sort 包)笨触,然后可以使用切片的 for-range 方法打印出所有的 key 和 value

方法相關(guān)小技巧?

在 Go 中雹舀,類型就是類(數(shù)據(jù)和關(guān)聯(lián)的方法)芦劣。Go 不知道類似面向?qū)ο笳Z言的類繼承的概念。繼承有兩個(gè)好處:代碼復(fù)用和多態(tài)说榆。

在 Go 中虚吟,代碼復(fù)用通過組合和委托實(shí)現(xiàn),多態(tài)通過接口的使用來實(shí)現(xiàn):有時(shí)這也叫 組件編程(Component Programming)签财。

許多開發(fā)者說相比于類繼承串慰,Go 的接口提供了更強(qiáng)大、卻更簡單的多態(tài)行為唱蒸。

讀寫數(shù)據(jù)邦鲫?

如何讀寫一個(gè)文件?

從命令行讀入數(shù)據(jù)神汹?

文件拷貝 io.Copy()

從命令行讀取參數(shù) : os.Args[1:]

os.OpenFile outputWriter := bufio.NewWriter(outputFile)

https://learnku.com/docs/the-way-to-go/122-file-reading-and-writing/3662

錯(cuò)誤處理庆捺?

永遠(yuǎn)不要忽略錯(cuò)誤,否則可能會(huì)導(dǎo)致程序崩潰Fㄎ骸滔以!

創(chuàng)建error: fmt.Errorf() / errors.New()

當(dāng)發(fā)生像數(shù)組下標(biāo)越界或類型斷言失敗這樣的運(yùn)行錯(cuò)誤時(shí),Go 運(yùn)行時(shí)會(huì)觸發(fā)運(yùn)行時(shí) panic蚁堤,伴隨著程序的崩潰拋出一個(gè) runtime.Error 接口類型的值醉者。這個(gè)錯(cuò)誤值有個(gè) RuntimeError() 方法用于區(qū)別普通錯(cuò)誤。

panic 會(huì)導(dǎo)致棧被展開直到 defer 修飾的 recover () 被調(diào)用或者程序中止披诗。

package main

import (
    "fmt"
)

func badCall() {
    panic("bad end")
}

func test() {
    defer func() {
        if e := recover(); e != nil {
            fmt.Printf("Panicing %s\r\n", e)
        }
    }()
    badCall()
    fmt.Printf("After bad call\r\n") // <-- wordt niet bereikt
}

func main() {
    fmt.Printf("Calling test\r\n")
    test()
    fmt.Printf("Test completed\r\n")
}

Calling test
Panicing bad end
Test completed

自定義包中的錯(cuò)誤處理和 panicking?

這是所有自定義包實(shí)現(xiàn)者應(yīng)該遵守的最佳實(shí)踐:

1)在包內(nèi)部,總是應(yīng)該從 panic 中 recover:不允許顯式的超出包范圍的 panic ()

2)向包的調(diào)用者返回錯(cuò)誤值(而不是 panic)立磁。

在包內(nèi)部呈队,特別是在非導(dǎo)出函數(shù)中有很深層次的嵌套調(diào)用時(shí),對(duì)主調(diào)函數(shù)來說用 panic 來表示應(yīng)該被翻譯成錯(cuò)誤的錯(cuò)誤場景是很有用的(并且提高了代碼可讀性)唱歧。

包內(nèi)panic宪摧;包外error

// parse.go
package parse

import (
    "fmt"
    "strings"
    "strconv"
)

// A ParseError indicates an error in converting a word into an integer.
type ParseError struct {
    Index int      // The index into the space-separated list of words.
    Word  string   // The word that generated the parse error.
    Err error // The raw error that precipitated this error, if any.
}

// String returns a human-readable error message.
func (e *ParseError) String() string {
    return fmt.Sprintf("pkg parse: error parsing %q as int", e.Word)
}

// Parse parses the space-separated words in in put as integers.
func Parse(input string) (numbers []int, err error) {
    defer func() {
        if r := recover(); r != nil {
            var ok bool
            err, ok = r.(error)
            if !ok {
                err = fmt.Errorf("pkg: %v", r)
            }
        }
    }()

    fields := strings.Fields(input)
    numbers = fields2numbers(fields)
    return
}

func fields2numbers(fields []string) (numbers []int) {
    if len(fields) == 0 {
        panic("no words to parse")
    }
    for idx, field := range fields {
        num, err := strconv.Atoi(field)
        if err != nil {
            panic(&ParseError{idx, field, err})
        }
        numbers = append(numbers, num)
    }
    return
}

使用一種用閉包處理錯(cuò)誤的模式?

func check(err error) { if err != nil { panic(err) } }

// errorhandler:這是一個(gè)包裝函數(shù)颅崩。接收一個(gè) fType1 類型的函數(shù) fn 并返回一個(gè)調(diào)用 fn 的函數(shù)几于。里面就包含有 defer/recover 機(jī)制
func errorHandler(fn fType1) fType1 {
    return func(a type1, b type2) {
        defer func() {
            if err, ok := recover().(error); ok {
                log.Printf(“run time panic: %v”, err)
            }
        }()
        fn(a, b)
    }
}

如何啟動(dòng)外部命令和程序?

os 包有一個(gè) StartProcess 函數(shù)可以調(diào)用或啟動(dòng)外部系統(tǒng)命令和二進(jìn)制可執(zhí)行文件沿后;它的第一個(gè)參數(shù)是要運(yùn)行的進(jìn)程沿彭,第二個(gè)參數(shù)用來傳遞選項(xiàng)或參數(shù),第三個(gè)參數(shù)是含有系統(tǒng)環(huán)境基本信息的結(jié)構(gòu)體尖滚。

這個(gè)函數(shù)返回被啟動(dòng)進(jìn)程的 id(pid)喉刘,或者啟動(dòng)失敗返回錯(cuò)誤瞧柔。

exec 包中也有同樣功能的更簡單的結(jié)構(gòu)體和函數(shù);主要是 exec.Command(name string, arg ...string)Run()睦裳。首先需要用系統(tǒng)命令或可執(zhí)行文件的名字創(chuàng)建一個(gè) Command 對(duì)象造锅,然后用這個(gè)對(duì)象作為接收者調(diào)用 Run()

單元測(cè)試和基準(zhǔn)測(cè)試?

寫測(cè)試用例

不要通過共享內(nèi)存來通信廉邑,而通過通信來共享內(nèi)存

xxx

通道channel

一個(gè)無緩沖通道只能包含 1 個(gè)元素哥蔚,有時(shí)顯得很局限。我們給通道提供了一個(gè)緩存蛛蒙,可以在擴(kuò)展的 make 命令中設(shè)置它的容量

func compute(ch chan int){
    ch <- someComputation() // when it completes, signal on the channel.
}

func main(){
    ch := make(chan int)    // allocate a channel.
    go compute(ch)      // stat something in a goroutines
    doSomethingElseForAWhile()
    result := <- ch
}

https://learnku.com/docs/the-way-to-go/142-covariance-channel/3686

學(xué)習(xí)研究下素?cái)?shù)打印這個(gè)函數(shù)

后臺(tái)服務(wù)模式:

// Backend goroutine.
func backend() {
    for {
        select {
        case cmd := <-ch1:
            // Handle ...
        case cmd := <-ch2:
            ...
        case cmd := <-chStop:
            // stop server
        }
    }
}

協(xié)程與恢復(fù)

func server(workChan <-chan *Work) {
    for work := range workChan {
        go safelyDo(work)   // start the goroutine for that work
    }
}

func safelyDo(work *Work) {
    defer func() {
        if err := recover(); err != nil {
            log.Printf("Work failed with %s in %v", err, work)
        }
    }()
    do(work)
}

使用鎖還是通道肺素?

  • 使用鎖的情景:
    • 訪問共享數(shù)據(jù)結(jié)構(gòu)中的緩存信息
    • 保存應(yīng)用程序上下文和狀態(tài)信息數(shù)據(jù)
  • 使用通道的情景:
    • 與異步操作的結(jié)果進(jìn)行交互
    • 分發(fā)任務(wù)
    • 傳遞數(shù)據(jù)所有權(quán)

惰性生成器的實(shí)現(xiàn)?

package main
import (
    "fmt"
)

type Any interface{}
type EvalFunc func(Any) (Any, Any)

func main() {
    evenFunc := func(state Any) (Any, Any) {
        os := state.(int)
        ns := os + 2
        return os, ns
    }

    even := BuildLazyIntEvaluator(evenFunc, 0)

    for i := 0; i < 10; i++ {
        fmt.Printf("%vth even: %v\n", i, even())
    }
}

func BuildLazyEvaluator(evalFunc EvalFunc, initState Any) func() Any {
    retValChan := make(chan Any)
    loopFunc := func() {
        var actState Any = initState
        var retVal Any
        for {
            retVal, actState = evalFunc(actState)
            retValChan <- retVal
        }
    }
    retFunc := func() Any {
        return <- retValChan
    }
    go loopFunc()
    return retFunc
}

func BuildLazyIntEvaluator(evalFunc EvalFunc, initState Any) func() int {
    ef := BuildLazyEvaluator(evalFunc, initState)
    return func() int {
        return ef().(int)
    }
}

使用通道實(shí)現(xiàn)Future模式宇驾。類似于java里面的future倍靡?

每一個(gè)協(xié)程執(zhí)行完返回一個(gè)channel對(duì)象,然后最后從每一個(gè)channel中讀取數(shù)據(jù)课舍。

package gorout

type Matrix struct {

}

func InverseProduct(a Matrix, b Matrix) Matrix{
    a_inv_future := InverseFuture(a)   // start as a goroutine
    b_inv_future := InverseFuture(b)   // start as a goroutine
    a_inv := <-a_inv_future
    b_inv := <-b_inv_future
    return Product(a_inv, b_inv)
}

func InverseFuture(a Matrix)  (chan Matrix){
    future := make(chan Matrix)
    go func() {
        future <- Inverse(a)
    }()
    return future
}

func Inverse(a Matrix) Matrix{
    return a
}

func Product(a,b Matrix) (c Matrix){
    return c
}

如何設(shè)計(jì)良好的錯(cuò)誤處理塌西,避免錯(cuò)誤檢測(cè)使代碼變得混亂?

使用recover來終止panic筝尾。

使用閉包的方式解決錯(cuò)誤捡需。這樣子啊子程序中錯(cuò)誤處理簡化成一個(gè)check(); 最后包裝一層筹淫。

func check(err error) { if err != nil { panic(err) } }

func errorHandler(fn fType1) fType1 {
    return func(a type1, b type2) {
        defer func() {
            if err, ok := recover().(error); ok {
                log.Printf(“run time panic: %v”, err)
            }
        }()
        fn(a, b)
    }
}

結(jié)構(gòu)體初始化站辉?

當(dāng)結(jié)構(gòu)體的命名以大寫字母開頭時(shí),該結(jié)構(gòu)體在包外可見损姜。

通常情況下饰剥,為每個(gè)結(jié)構(gòu)體定義一個(gè)構(gòu)建函數(shù),并推薦使用構(gòu)建函數(shù)初始化結(jié)構(gòu)體

如何通過一個(gè)通道讓主程序等待直到協(xié)程完成摧阅?(信號(hào)量模式)

ch := make(chan int) // Allocate a channel.
// Start something in a goroutine; when it completes, signal on the channel.
go func() {
    // doSomething
    ch <- 1 // Send a signal; value does not matter.
}()
doSomethingElseForAWhile()
<-ch // Wait for goroutine to finish; discard sent value.

簡單的超時(shí)模版汰蓉?

timeout := make(chan bool, 1)
go func() {
    time.Sleep(1e9) // one second  
    timeout <- true
}()
select {
    case <-ch:
    // a read from ch has occurred
    case <-timeout:
    // the read from ch has timed out
}

如何使用輸入通道和輸出通道代替鎖?

func Worker(in, out chan *Task) {
    for {
        t := <-in
        process(t)
        out <- t
    }
}

如何取消取消耗時(shí)很長的同步調(diào)用棒卷?

ch := make(chan error, 1)
go func() { ch <- client.Call("Service.Method", args, &reply) } ()
select {
case resp := <-ch
    // use resp and reply
case <-time.After(timeoutNs):
    // call timed out
    break
}

如何在程序出錯(cuò)時(shí)顾孽,終止程序?

if err != nil {
   fmt.Printf(“Program stopping with error %v”, err)
   os.Exit(1)
}

// or
if err != nil { 
    panic(“ERROR occurred: “ + err.Error())
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末比规,一起剝皮案震驚了整個(gè)濱河市若厚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜒什,老刑警劉巖测秸,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡乞封,警方通過查閱死者的電腦和手機(jī)做裙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肃晚,“玉大人锚贱,你說我怎么就攤上這事」卮” “怎么了拧廊?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長晋修。 經(jīng)常有香客問我吧碾,道長,這世上最難降的妖魔是什么墓卦? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任倦春,我火速辦了婚禮,結(jié)果婚禮上落剪,老公的妹妹穿的比我還像新娘睁本。我一直安慰自己,他們只是感情好忠怖,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布呢堰。 她就那樣靜靜地躺著,像睡著了一般凡泣。 火紅的嫁衣襯著肌膚如雪枉疼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天鞋拟,我揣著相機(jī)與錄音骂维,去河邊找鬼。 笑死严卖,一個(gè)胖子當(dāng)著我的面吹牛席舍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哮笆,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼汰扭!你這毒婦竟也來了稠肘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤萝毛,失蹤者是張志新(化名)和其女友劉穎项阴,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡环揽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年略荡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歉胶。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汛兜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出通今,到底是詐尸還是另有隱情粥谬,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布辫塌,位于F島的核電站漏策,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏臼氨。R本人自食惡果不足惜掺喻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望储矩。 院中可真熱鬧感耙,春花似錦、人聲如沸椰苟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舆蝴。三九已至谦絮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間洁仗,已是汗流浹背层皱。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赠潦,地道東北人叫胖。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像她奥,于是被迫代替她去往敵國和親瓮增。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345