網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
Examples
在添加新包時(shí),包括預(yù)期使用的示例:
- 一個(gè)可運(yùn)行的示例
- 或一個(gè)演示完整調(diào)用序列的簡(jiǎn)單測(cè)試
Goroutine Lifetimes
當(dāng)你使用 goroutines
時(shí),要明確它們何時(shí)退出或者是否退出。
goroutine
可以通過(guò)阻塞通道發(fā)送或接收而泄漏: 即使阻塞的通道不可達(dá),垃圾收集器也不會(huì)終止 goroutine
。
即使 goroutine
沒(méi)有泄漏,當(dāng)它們不再被需要時(shí),將它們留在空中也會(huì)導(dǎo)致其他微妙和難以診斷的問(wèn)題。發(fā)送在已關(guān)閉的通道時(shí)會(huì)引發(fā) panic
。“在結(jié)果不需要之后”修改仍在使用的輸入仍然會(huì)導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)。并且讓 goroutines
在運(yùn)行中停留任意長(zhǎng)的時(shí)間會(huì)導(dǎo)致不可預(yù)測(cè)的內(nèi)存使用。
盡量保持并發(fā)代碼足夠簡(jiǎn)單,這樣 goroutine
的生存期就很明顯了。如果這是不可行的,記錄下 goroutines
退出的時(shí)間和原因。
有些時(shí)候我們使用 goroutine 的時(shí)候,會(huì)忘記捕獲 panic,這里提供一個(gè)封裝好了的 go
方法:
// GoSafe runs the given fn using another goroutine, recovers if fn panics. func GoSafe(log *log.Logger, fn func()) { go RunSafe(log, fn) } // RunSafe runs the given fn, recovers if fn panics. func RunSafe(log *log.Logger, fn func()) { defer Recover(log) fn() } // Recover is used with defer catch panics. func Recover(log *log.Logger) { if p := recover(); p != nil { log.Error("%s\n%s", p, debug.Stack()) } }
Handle Errors
不要使用 _
變量丟棄錯(cuò)誤。如果函數(shù)返回錯(cuò)誤,請(qǐng)檢查它以確保函數(shù)成功。處理錯(cuò)誤,返回錯(cuò)誤,或者,在真正異常的情況下,拋出 panic
。
可以參考官方文檔中優(yōu)雅的做法:Effective Go
import
避免重命名導(dǎo)入,除非是為了避免名稱沖突
好的包名不需要重命名。如果發(fā)生沖突,最好重命名、本地的或特定于項(xiàng)目的導(dǎo)入。
導(dǎo)入按組區(qū)分,組與組之間有空白行。標(biāo)準(zhǔn)庫(kù)包總是在第一組中。
package main import ( "fmt" "hash/adler32" "os" "appengine/foo" "appengine/user" "github.com/foo/bar" "rsc.io/goversion/version" )
使用 goimport 能幫助到您,所以在項(xiàng)目中配置 goimport 是必要的。
Import Blank
僅為其副作用而導(dǎo)入的包(使用import _ "pkg"語(yǔ)法)應(yīng)該只在程序的主包中導(dǎo)入,或在需要它們的測(cè)試中導(dǎo)入。
類似使用其他包的 init
函數(shù)
Import Dot
import .
在解決循環(huán)依賴時(shí)較為好用的方式,但是不能濫用:
package foo_test import ( "bar/testutil" // also imports "foo" . "foo" )
在這種情況下,測(cè)試文件不能在包 foo
中,因?yàn)樗褂?bar/testutil
,它導(dǎo)入 foo
。所以我們使用 'import .
' 形式讓文件假裝是包 foo
的一部分,即使它不是。除了這種情況,不要使用import .
在你的項(xiàng)目中。它使程序更加難以閱讀,因?yàn)椴磺宄?Quux
這樣的名稱是當(dāng)前包還是導(dǎo)入包中的頂級(jí)標(biāo)識(shí)符。
In-Band Errors
在 C
和類似的語(yǔ)言中,函數(shù)通常會(huì)返回 -1 或 null 這樣的值來(lái)表示錯(cuò)誤或默認(rèn)結(jié)果:
// Lookup returns the value for key or "" if there is no mapping for key. func Lookup(key string) string // Failing to check for an in-band error value can lead to bugs: Parse(Lookup(key)) // returns "parse failure for value" instead of "no value for key"
Go
對(duì)多個(gè)返回值的支持提供了更好的解決方案。與其要求客戶端檢查帶內(nèi)錯(cuò)誤值,函數(shù)應(yīng)該返回一個(gè)額外的值來(lái)指示它的其他返回值是否有效。這個(gè)返回值可能是一個(gè)錯(cuò)誤,也可能是一個(gè)不需要解釋的布爾值。它應(yīng)該是最終的返回值。
// Lookup returns the value for key or ok=false if there is no mapping for key. func Lookup(key string) (value string, ok bool)
這可以防止調(diào)用者錯(cuò)誤地使用返回值:
Parse(Lookup(key)) // compile-time error
并鼓勵(lì)更健壯和可讀的代碼:
value, ok := Lookup(key) if !ok { return fmt.Errorf("no value for %q", key) } return Parse(value)
該規(guī)則適用于導(dǎo)出函數(shù),但也適用于私有函數(shù)。
當(dāng)它們是函數(shù)的有效結(jié)果時(shí),像 nil、""、0 和 -1 這樣的值是可以的,也就是說(shuō),當(dāng)調(diào)用者不需要以不同于其他值的方式處理它們時(shí)。
一些標(biāo)準(zhǔn)庫(kù)函數(shù),比如包 strings
中的函數(shù),返回帶內(nèi)錯(cuò)誤值。這極大地簡(jiǎn)化了字符串操作代碼,但代價(jià)是需要程序員付出更多努力。一般來(lái)說(shuō),Go代碼應(yīng)該返回額外的錯(cuò)誤值。
Indent Error Flow
盡量將正常的代碼路徑縮進(jìn)到最小,并縮進(jìn)錯(cuò)誤處理,首先處理它。這通過(guò)允許快速視覺(jué)掃描正常路徑提高了代碼的可讀性。例如,不要寫(xiě):
if err != nil { // error handling } else { // normal code }
相反,寫(xiě):
if err != nil { // error handling return // or continue, etc. } // normal code
如果語(yǔ)句中有初始化語(yǔ)句,例如:if
if x, err := f(); err != nil { // error handling return } else { // use x }
然后,這可能需要將短變量聲明移動(dòng)到它自己的行:
x, err := f() if err != nil { // error handling return } // use x
Initialisms
名字中屬于首字母縮寫(xiě)或首字母縮寫(xiě)的詞(例如:"URL"
或"NATO"
)的大小寫(xiě)是一致的。例如,"URL"
應(yīng)該顯示為 "URL"
或 "url"
(如"urlPony"
或"URLPony"
),而不是"Url"
。例如:ServeHTTP
而不是ServeHttp
。對(duì)于具有多個(gè)初始化“單詞”的標(biāo)識(shí)符,可以使用例如"xmlHTTPRequest"
或"XMLHTTPRequest"
。
當(dāng)"ID"
是"identifier"
的縮寫(xiě)時(shí),這個(gè)規(guī)則也適用于"ID"
(在大多數(shù)情況下,它不是"ego"
、"superego"
中的"ID"
),所以寫(xiě)"appID"
而不是"appId"
。
protobuf
生成的代碼不受此規(guī)則約束。人類編寫(xiě)的代碼要比機(jī)器編寫(xiě)的代碼具有更高的標(biāo)準(zhǔn)。
Interfaces
Go
接口通常屬于使用接口類型值的包,而不是實(shí)現(xiàn)這些值的包。實(shí)現(xiàn)包應(yīng)該返回具體的(通常是指針或結(jié)構(gòu))類型:這樣,新的方法就可以添加到實(shí)現(xiàn)中,而不需要大量的重構(gòu)。
不要在API的實(shí)現(xiàn)者端定義 "mock"
接口;相反,應(yīng)該設(shè)計(jì)API,以便可以使用實(shí)際實(shí)現(xiàn)的公共API進(jìn)行測(cè)試。
不要在使用之前定義接口:沒(méi)有一個(gè)實(shí)際的使用示例,很難判斷接口是否必要,更不用說(shuō)它應(yīng)該包含哪些方法了。(有點(diǎn)重構(gòu)的意思,將已有的方法抽象起來(lái))
package consumer // consumer.go type Thinger interface { Thing() bool } func Foo(t Thinger) string { … }
package consumer // consumer_test.go type fakeThinger struct{ … } func (t fakeThinger) Thing() bool { … } … if Foo(fakeThinger{…}) == "x" { … }
// DO NOT DO IT!!! package producer type Thinger interface { Thing() bool } type defaultThinger struct{ … } func (t defaultThinger) Thing() bool { … } func NewThinger() Thinger { return defaultThinger{ … } }
相反,返回一個(gè)具體類型,并讓使用者模擬生產(chǎn)者實(shí)現(xiàn)。
package producer type Thinger struct{ … } func (t Thinger) Thing() bool { … } func NewThinger() Thinger { return Thinger{ … } }
Line Length
在Go代碼中沒(méi)有嚴(yán)格的行長(zhǎng)限制,但是要避免令人不舒服的長(zhǎng)行。類似地,當(dāng)行更長(zhǎng)的時(shí)候,不要添加換行符以保持行短——例如,如果它們是重復(fù)的。
大多數(shù)情況下,當(dāng)人們“不自然地”換行時(shí)(或多或少地在函數(shù)調(diào)用或函數(shù)聲明中換行,盡管存在一些異常),如果它們有合理數(shù)量的參數(shù)和合理較短的變量名,換行將是不必要的。較長(zhǎng)的行似乎和較長(zhǎng)的名字聯(lián)系在一起,去掉長(zhǎng)名字會(huì)有很大幫助。
換句話說(shuō),斷行是因?yàn)槟闼鶎?xiě)的內(nèi)容的語(yǔ)義(作為一般規(guī)則),而不是因?yàn)樾虚L(zhǎng)。如果您發(fā)現(xiàn)這產(chǎn)生了太長(zhǎng)行,那么更改名稱或語(yǔ)義,您可能會(huì)得到一個(gè)好的結(jié)果。
實(shí)際上,這與函數(shù)的長(zhǎng)度是完全相同的。沒(méi)有“函數(shù)永遠(yuǎn)不會(huì)超過(guò)N行的規(guī)則”,但確實(shí)存在過(guò)長(zhǎng)的函數(shù),以及重復(fù)過(guò)小的函數(shù),解決方法是改變函數(shù)的邊界,而不是開(kāi)始計(jì)數(shù)行數(shù)。
Mixed Caps
Effective Go
Named Result Parameters
想想在 godoc
中會(huì)是什么樣子。命名結(jié)果參數(shù)如下:
func (n *Node) Parent1() (node *Node) {} func (n *Node) Parent2() (node *Node, err error) {}
將在 godoc
中重復(fù);更好的使用:
func (n *Node) Parent1() *Node {} func (n *Node) Parent2() (*Node, error) {}
另一方面,如果函數(shù)返回兩個(gè)或三個(gè)相同類型的參數(shù),或者如果從上下文不清楚結(jié)果的含義,添加名稱在某些上下文中可能是有用的。不要為了避免在函數(shù)內(nèi)部聲明 var 而命名結(jié)果參數(shù);以不必要的API冗長(zhǎng)為代價(jià)來(lái)?yè)Q取較小的實(shí)現(xiàn)簡(jiǎn)便性。
func (f *Foo) Location() (float64, float64, error)
不如:
// Location returns f's latitude and longitude. // Negative values mean south and west, respectively. func (f *Foo) Location() (lat, long float64, err error)
如果函數(shù)只有幾行,裸返回是可以的。一旦它是一個(gè)中等大小的函數(shù),就要顯式地顯示返回值。推論:僅僅因?yàn)榻Y(jié)果參數(shù)允許使用裸返回而命名結(jié)果參數(shù)是不值得的。清晰的文檔總是比在函數(shù)中節(jié)省一兩行更重要。
最后,在某些情況下,您需要命名結(jié)果參數(shù),以便在延遲閉包中更改它。這總是可以的。
Naked Returns
沒(méi)有參數(shù)的語(yǔ)句返回指定的返回值。這就是所謂的“裸返回”。
func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return }
Package Comments
包注釋,像 godoc
提供的所有注釋一樣,必須出現(xiàn)在 package
的上面,不能有空行。
// Package math provides basic constants and mathematical functions. package math
/* Package template implements data-driven templates for generating textual output such as HTML. .... */ package template
對(duì)于 "package main" 注釋,其他風(fēng)格的注釋在二進(jìn)制名稱之后也可以(如果它在前面,則可以大寫(xiě)),例如,對(duì)于您可以編寫(xiě)的目錄中的
// Binary seedgen ... package main
// Command seedgen ... package main
// Program seedgen ... package main
// The seedgen command ... package main
// The seedgen program ... package main
// Seedgen .. package main
這些都是例子,合理的變體也是可以接受的。
注意,以小寫(xiě)單詞開(kāi)頭的句子不屬于包注釋的可接受選項(xiàng),因?yàn)樗鼈兪枪_(kāi)可見(jiàn)的,應(yīng)該用正確的英語(yǔ)編寫(xiě),包括句子的第一個(gè)單詞大寫(xiě)。當(dāng)二進(jìn)制名稱是第一個(gè)單詞時(shí),即使它與命令行調(diào)用的拼寫(xiě)不嚴(yán)格匹配,也需要將其大寫(xiě)。
Effective Go
struct
每個(gè)結(jié)構(gòu)體必須有自己的構(gòu)造函數(shù),并且使用 options 模式來(lái)構(gòu)建新的參數(shù)。
原文鏈接:https://juejin.cn/post/7159010177092419620
相關(guān)推薦
- 2022-03-30 C語(yǔ)言實(shí)現(xiàn)猜數(shù)字小項(xiàng)目_C 語(yǔ)言
- 2022-07-03 Golang之理解錯(cuò)誤輸出
- 2022-05-18 TypeScript中的接口和泛型你了解嗎_基礎(chǔ)知識(shí)
- 2022-05-12 Kotlin 代數(shù)/枚舉/密封類
- 2022-07-06 python中csv文件創(chuàng)建、讀取及修改等操作實(shí)例_python
- 2023-08-01 在 TypeScript 中 interface 和 type 的區(qū)別
- 2022-12-16 C++?Boost?EnableIf函數(shù)使用介紹_C 語(yǔ)言
- 2022-09-22 Python 閉包與裝飾器
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支