หลีกเลี่ยงบรรทัดที่ยาวเกินไป
หลีกเลี่ยงการใช้บรรทัดโค้ดที่ทำให้ผู้อ่านต้องเลื่อนตำแหน่งในแนวนอนหรือหมุนเอกสารอย่างมาก
เราแนะนำให้ จำกัดความยาวของบรรทัดไว้ที่ 99 ตัวอักษร ผู้เขียนควรแบ่งบรรทัดก่อนขีดจำกัดนี้ แต่มันไม่ใช่กฎหมายแข็งแรง สามารถยอมให้โค้ดเกินขีดจำกัดนี้ได้
ความสม่ำเสมอ
มาตรฐานบางส่วนที่ระบุไว้ในเอกสารนี้ขึ้นอยู่กับการพิจารณามีอารมณ์ สถานการณ์ หรือบริบท อย่างไรก็ตาม สิ่งสำคัญที่สุดคือการ รักษาความสม่ำเสมอ
โค้ดที่สม่ำเสมอทำให้การเคลื่อนไหวและการส่งเสริมที่เพิ่มมีฐานที่มั่นมากขึ้น ต้องการค่าใช้จ่ายในการเรียนรู้น้อยลง มีความจำเป็นที่น้อยลง และง่ายต่อการย้ายที่ อัพเดท แก้ไขข้อผิดพลาดเมื่อมีการปฏิบัติตามข้อบังคับใหม่หรือเมื่อเกิดข้อผิดพลาด
ในทางกลับกันการรวมรูปแบบของโค้ดที่แตกต่างหรือขัดแย้งกันมากมายในโค้ดเบส จะทำให้ค่าใช้จ่ายในการเรียงรูปมากขึ้น ความไม่แน่นอน และความเอาใจใส่
โดยตรง ผลรวมของมั่นใจของฉัน อันนี้สร้างสรรค์ให้ความเร็วลดลง การทบทวนโค้ดอันทรยศ และจำนวนของข้อผิดพลาดที่เพิ่มขึ้น
เมื่อนำมาใช้เครื่องหมายสำหรับโค้ดเบส แนะนำให้เปลี่ยนแปลงในช่วงแพแก้ไข ที่ระดับแพ็คเกจโปรโตคอล (หรือขนาดที่ใหญ่กว่า) การปรับใช้สไตส์หลายรูปแบบที่ระดับภายใต้แพ็คเกจทนไม่ได้กับข้อความที่อยู่ข้างต้น
กลุ่มการประกาศที่คล้ายกัน
ภาษา Go รองรับการกลุ่มการประกาศที่คล้ายกัน
ไม่แนะนำ:
import "a"
import "b"
แนะนำ:
import (
"a"
"b"
)
การใช้กับค่าคงที่ ตัวแปร และประเภทการประกาศ :
ไม่แนะนำ:
const a = 1
const b = 2
var a = 1
var b = 2
type Area float64
type Volume float64
แนะนำ:
const (
a = 1
b = 2
)
var (
a = 1
b = 2
)
type (
Area float64
Volume float64
)
เพียงแค่กลุ่มการประกาศที่เกี่ยวข้องเท่านั้นและหลีกเลี่ยงการรวมกันเป็นกลุ่ม
ไม่แนะนำ:
type Operation int
const (
Add Operation = iota + 1
Subtract
Multiply
EnvVar = "MY_ENV"
)
แนะนำ:
type Operation int
const (
Add Operation = iota + 1
Subtract
Multiply
)
const EnvVar = "MY_ENV"
ไม่มีข้อแบงจำเก็ยการใช้กลุ่มที่นี่ ตัวอย่างเช่น คุณสามารถนำมาใช้เข้าชื่อในฟังก์ชัน ไม่แนะนำ:
func f() string {
red := color.New(0xff0000)
green := color.New(0x00ff00)
blue := color.New(0x0000ff)
...
}
แนะนำ:
func f() string {
var (
red = color.New(0xff0000)
green = color.New(0x00ff00)
blue = color.New(0x0000ff)
)
...
}
Exception: หากการประกาศตัวแปรติดตายกับตัวแปรที่เกี่ยวข้อง โดยเฉพาะศัพท์ที่มีการประกาศตัวแปรที่ไม่เกี่ยวข้องร่วมกัน หลังจากนั้นให้ทำการรวมควบคงให้รวมรวมรวมทั้งหมด
ไม่แนะนำ:
func (c *client) request() {
caller := c.name
format := "json"
timeout := 5*time.Second
var err error
// ...
}
แนะนำ:
func (c *client) request() {
var (
caller = c.name
format = "json"
timeout = 5*time.Second
err error
)
// ...
}
การจัดกลุ่มการนำเข้า
การนำเข้าควรจะถูกจัดเป็นกลุ่มเป็น 2 ประเภท:
- คลังสตาแดด
- คลังสหรัง
โดยค่าเริ่มต้นนี้คือการจัดกลุ่มสำหรับ goimports
ไม่แนะนำ:
import (
"fmt"
"os"
"go.uber.org/atomic"
"golang.org/x/sync/errgroup"
)
แนะนำ:
import (
"fmt"
"os"
"go.uber.org/atomic"
"golang.org/x/sync/errgroup"
)
ชื่อแพคเกจ
เมื่อต้องการให้ชื่อแพ็คเกจ กรุณาจำไว้ว่า
- ต้องเป็นตัวพิมพ์เล็ก ไม่มีตัวอักษรใหญ่หรือเครื่องหมาย underscore
- ในกรณีมากมายมักไม่จำเป็นต้องเปลี่ยนชื่อในขณะนำเข้า
- สั้นและกระชับ จำไว้ว่าชื่อที่เป็นความเป็นจริงสมบูรณ์ทุกที่ที่นำมาใช้
- หลีกเลี่ยงรูปนามพลาย ตัวอย่างเช่น ใช้
net/url
แทนที่จะใช้net/urls
. - หลีกเลี่ยงการใช้ "common," "util," "shared," หรือ "lib." เหล่านี้ไม่ให้คำแนะนำอยู่ในระดับ
การตั้งชื่อฟังก์ชัน
เรายึดถือตามแนวปฏิบัติของชุมชน Go ในการใช้ MixedCaps สำหรับชื่อฟังก์ชัน ยกเว้นกรณีของการจัดกลุ่มกรณีทดสอบที่เกี่ยวข้อง ที่ฟังก์ชันชื่ออาจมีเส้นใต้ เช่น: TestMyFunction_WhatIsBeingTested
การตั้งชื่อนามแฝง
หากชื่อแพ็กเกจไม่ตรงกับองค์ประกอบสุดท้ายของเส้นทางนำเข้า จะต้องใช้ชื่อนามแฝงในการนำเข้า
import (
"net/http"
client "example.com/client-go"
trace "example.com/trace/v2"
)
ในกรณีอื่น ๆ ควรหลีกเลี่ยงการใช้ชื่อนามแฝง น้อยที่สุด ยกเว้นกรณีที่มีการขัดแย้งโดยตรงระหว่างการนำเข้า ไม่แนะนำ:
import (
"fmt"
"os"
nettrace "golang.net/x/trace"
)
แนะนำ:
import (
"fmt"
"os"
"runtime/trace"
nettrace "golang.net/x/trace"
)
การจัดกลุ่มและการเรียงลำดับของฟังก์ชัน
- ฟังก์ชันควรถูกระบุลำดับโดยประมาณตามลำดับที่เรียกใช้
- ฟังก์ชันภายในไฟล์เดียวกันควรจัดกลุ่มตามผู้รับ
จึงจะต้องปรากฏในไฟล์อยู่หลังจากการนิยาม
struct
const
และvar
newXYZ()
/NewXYZ()
อาจปรากฏหลังจากการกำหนดประเภท แต่ก่อนเมทอดที่เหลือของผู้รับ
เนื่องจากฟังก์ชันถูกจัดกลุ่มตามผู้รับ ฟังก์ชันทั่วไปควรปรากฏที่สุดท้ายของไฟล์ ไม่แนะนำ:
func (s *something) Cost() {
return calcCost(s.weights)
}
type something struct{ ... }
func calcCost(n []int) int {...}
func (s *something) Stop() {...}
func newSomething() *something {
return &something{}
}
แนะนำ:
type something struct{ ... }
func newSomething() *something {
return &something{}
}
func (s *something) Cost() {
return calcCost(s.weights)
}
func (s *something) Stop() {...}
func calcCost(n []int) int {...}
ลดคำพูน
โค้ดควรลดการซ้อนทำการจัดการข้อผิดพลาด/กรณีพิเศษโดยเร็วที่สุดและทำการส่งคืนหรือทำการวนซ้ำต่อไป การลดการซ้อนลดจำนวนโค้ดในระดับหลาย ๆ
ไม่แนะนำ:
for _, v := range data {
if v.F1 == 1 {
v = process(v)
if err := v.Call(); err == nil {
v.Send()
} else {
return err
}
} else {
log.Printf("Invalid v: %v", v)
}
}
แนะนำ:
for _, v := range data {
if v.F1 != 1 {
log.Printf("Invalid v: %v", v)
continue
}
v = process(v)
if err := v.Call(); err != nil {
return err
}
v.Send()
}
else ที่ไม่จำเป็น
หากตัวแปรถูกกำหนดในทั้งสองสาขาของเงื่อนไข สามารถแทนที่ด้วยคำสั่ง if เดียว
ไม่แนะนำ:
var a int
if b {
a = 100
} else {
a = 10
}
แนะนำ:
a := 10
if b {
a = 100
}
การประกาศตัวแปรระดับสูงสุด
ในระดับสูงสุด ใช้คำสั่ง var
มาตราฐาน อย่าระบุประเภท น้อยที่สุด ถ้าไม่ต่างจากประเภทของนิพจน์
ไม่แนะนำ:
var _s string = F()
func F() string { return "A" }
แนะนำ:
var _s = F()
// เนื่องจาก F ส่งคืนประเภท string อย่างชัดเจน เราไม่จำเป็นต้องระบุประเภทสำหรับ _s
func F() string { return "A" }
กำหนดประเภทหากมีการตรงต่างจากประเภทของนิพจน์
type myError struct{}
func (myError) Error() string { return "error" }
func F() myError { return myError{} }
var _e error = F()
// F ส่งคืนตัวอย่างของประเภท myError แต่เราต้องการประเภท error
ใช้ '_' เป็นคำนำหน้าสำหรับค่าคงที่และตัวแปรระดับบนที่ไม่ได้ส่งออก
สำหรับค่าคงที่และตัวแปรระดับบนที่ไม่ได้ส่งออก (vars
และ consts
) ให้ใช้คำนำหน้าด้วยเส้นใต้ _
เพื่อแสดงถึงลักษณะทางโลกาศของพวกนี้เมื่อถูกใช้งาน
หลักการพื้นฐาน: ตัวแปรและค่าคงที่ระดับบนมีขอบเขตในแพ็คเกจ การใช้ชื่อทั่ว ๆ ไปอาจนำไปสู่การใช้ค่าที่ผิดพลาดในไฟล์อื่น ๆ
ไม่แนะนำ:
// foo.go
const (
defaultPort = 8080
defaultUser = "user"
)
// bar.go
func Bar() {
defaultPort := 9090
...
fmt.Println("Default port", defaultPort)
// เราจะไม่เห็นข้อผิดพลาดในการคอมไพล์ถ้าบรรทัดแรกของ Bar() ถูกลบ
}
แนะนำ:
// foo.go
const (
_defaultPort = 8080
_defaultUser = "user"
)
ข้อยกเว้น: ค่าข้อผิดพลาดที่ไม่ได้ส่งออกสามารถใช้คำนำหน้า err
โดยไม่ใช้เส้นใต้ ดูที่ตัวอย่างการตั้งชื่อข้อผิดพลาด
การฝังรวมในตัวโครงสร้าง
ประเภทที่ฝังรวม (เช่น mutex) ควรวางไว้ที่ด้านบนของรายการฟิลด์ในตัวโครงสร้าง และต้องมีบรรทัดว่างระหว่างฟิลด์ที่ถูกฝังรวมกับฟิลด์ปกติ
ไม่แนะนำ:
type Client struct {
version int
http.Client
}
แนะนำ:
type Client struct {
http.Client
version int
}
การฝังรวมควรให้ประโยชน์ที่ชัดเจน เช่น เพิ่มหรือเสริมฟังก์ชันในทางที่เหมาะสม ควรใช้โดยไม่มีผลกระทบที่แย่ต่อผู้ใช้ (ดูเพิ่มเติมที่: หลีกเลี่ยงการฝังรวมประเภทในตัวโครงสร้างสาธารณะ)
ข้อยกเว้น: แม้ในประเภทที่ไม่ส่งออก Mutex ไม่ควรใช้เป็นฟิลด์ที่ถูกฝังรวม ดูเพิ่มเติมที่: ค่าศูนย์ของ Mutex ถูกต้อง
การฝังรวม ไม่ควร:
- มีอยู่เพื่อความสวยงามหรือความสะดวก
- ทำให้ยากต่อการสร้างหรือใช้ประเภทภายนอก
- มีผลกระทบที่ค่าศูนย์ของประเภทภายนอก หากประเภทภายนอกมีค่าศูนย์ที่มีประโยชน์ ควรยังมีค่าศูนย์ที่มีประโยชน์หลังจากที่ฝังรวมประเภทภายใน
- มีผลกระทบที่ทำให้ฟังก์ชันหรือฟิลด์ที่ไม่เกี่ยวข้องจากประเภทที่ถูกฝังรวมถูกเปิดเผย
- มีการเปิดเผยประเภทที่ไม่ได้ส่งออก
- มีผลกระทบที่เปลี่ยนแปลงรูปแบบการกําหนดค่าภายในของประเภทภายใน
- ฝังรวมประเภทภายในไว้ในรูปแบบที่ไม่เป็นมาตรฐาน
- สามารถให้ผู้ใช้สังเกตหรือควบคุมประเภทภายใน
- เปลี่ยนแปลงพฤติกรรมทั่วไปของของฟังก์ชันภายในในทางที่อาจทําให้ผู้ใช้สับสน
โดยสรุปคือการฝังรวมอย่างไร้ความตั้งใจและวัตถุประสงค์ การทดสอบดีคือ "ฟังก์ชัน/ฟิลด์ที่ถูกเปิดเผยทั้งหมดจากประเภทภายในจะถูกเพิ่มโดยตรงเข้าสู่ประเภทภายนอกหรือไม่?" หากคําตอบเป็น บางส่วน
หรือ ไม่
, ไม่ควรฝังรวมประเภทภายใน - ใช้ฟิลด์แทน
ไม่แนะนำ:
type A struct {
// ไม่ดี: A.Lock() และ A.Unlock() ตอนนี้สามารถใช้งานได้
// ไม่มีประโยชน์ฟังก์ชันเปลี่ยนภาพและอนุญาตผู้ใช้ควบคุมรายละเอียดภายในของ A
sync.Mutex
}
แนะนำ:
type countingWriteCloser struct {
// ดี: Write() ถูกจัดการที่ระดับภายนอกสำหรับวัตถุประโยชน์เฉพาะ
// และมอบหมายงานให้กับ Write() ของประเภทภายใน
io.WriteCloser
count int
}
func (w *countingWriteCloser) Write(bs []byte) (int, error) {
w.count += len(bs)
return w.WriteCloser.Write(bs)
}
การประกาศตัวแปรภายในฟังก์ชัน
หากตัวแปรถูกกําหนดโดยชัดเจนให้ใช้รูปแบบการประกาศตัวแปรแบบสั้น (:=
)
ไม่แนะนำ:
var s = "foo"
แนะนำ:
s := "foo"
อย่างไรก็ตาม ในบางกรณีการใช้คำสำคัญ var
สําหรับค่าเริ่มต้นสามารถเป็นที่ชัดเจน
ไม่แนะนำ:
func f(list []int) {
filtered := []int{}
for _, v := range list {
if v > 10 {
filtered = append(filtered, v)
}
}
}
แนะนำ:
func f(list []int) {
var filtered []int
for _, v := range list {
if v > 10 {
filtered = append(filtered, v)
}
}
}
ค่า nil
เป็น slice ที่ถูกต้อง
nil
คือ slice ที่ถูกต้องที่มีความยาวเป็น 0 ซึ่งหมายความว่า:
- คุณไม่ควรส่งคืน slice ที่มีความยาวเป็นศูนย์โดยตรง แต่ควรส่งคืน
nil
ไม่แนะนำ:
if x == "" {
return []int{}
}
แนะนำ:
if x == "" {
return nil
}
- เพื่อตรวจสอบว่า slice ว่างหรือไม่ ควรใช้
len(s) == 0
เสมอ แทนที่จะใช้nil
ไม่แนะนำ:
func isEmpty(s []string) bool {
return s == nil
}
แนะนำ:
func isEmpty(s []string) bool {
return len(s) == 0
}
- Slice ค่าศูนย์ (slice ที่ประกาศด้วย
var
) สามารถใช้ได้ทันทีโดยไม่ต้องเรียกใช้make()
ไม่แนะนำ:
nums := []int{}
// หรือ nums := make([]int)
if add1 {
nums = append(nums, 1)
}
if add2 {
nums = append(nums, 2)
}
แนะนำ:
var nums []int
if add1 {
nums = append(nums, 1)
}
if add2 {
nums = append(nums, 2)
}
โปรดจำไว้ว่า แม้ว่า slice ที่เป็น nil
จะเป็น slice ที่ถูกต้อง แต่มันไม่เท่ากับ slice ที่มีความยาวเป็นศูนย์ (อันหนึ่งเป็น nil
และอีกอันหนึ่งไม่ใช่) และอาจถูกจัดการต่างกันในสถานการณ์ต่าง ๆ (เช่น การจัดลำดับ)
ขอบเขตตัวแปรที่ถูกจำกัด
หากเป็นไปได้ ลองจำกัดขอบเขตของตัวแปร ยกเว้นว่ามันขัดแย้งกับกฎในการลดการซ้อนทับ
ไม่แนะนำ:
err := os.WriteFile(name, data, 0644)
if err != nil {
return err
}
แนะนำ:
if err := os.WriteFile(name, data, 0644); err != nil {
return err
}
หากต้องใช้ผลลัพธ์ของการเรียกฟังก์ชันนอกจากคำสั่ง if statement อย่าพยายามจำกัดขอบเขต
ไม่แนะนำ:
if data, err := os.ReadFile(name); err == nil {
err = cfg.Decode(data)
if err != nil {
return err
}
fmt.Println(cfg)
return nil
} else {
return err
}
แนะนำ:
data, err := os.ReadFile(name)
if err != nil {
return err
}
if err := cfg.Decode(data); err != nil {
return err
}
fmt.Println(cfg)
return nil
หลีกเลี้ยงการส่งพารามิเตอร์อิ๊งไร้การห่อหุ้ม
การส่งพารามิเตอร์ที่ไม่ชัดเจนในการเรียกใช้ฟังก์ชันอาจทำให้ความอ่านลดลง หากความหมายของชื่อพารามิเตอร์ไม่ชัดเจน ให้เพิ่มคำอธิบายเพิ่มเติมแบบ C-style comment (/* ... */
) ที่พารามิเตอร์
ไม่แนะนำ:
// func printInfo(name string, isLocal, done bool)
printInfo("foo", true, true)
แนะนำ:
// func printInfo(name string, isLocal, done bool)
printInfo("foo", true /* isLocal */, true /* done */)
สำหรับตัวอย่างข้างต้น วิธีที่ดีกว่าอาจเป็นการแทนที่ประเภท bool
ด้วยประเภทที่กำหนดเอง โดยที่พารามิเตอร์สามารถรองรับได้มากกว่าแค่สองสถานะ (จริง/เท็จ) ในอนาคต
type Region int
const (
UnknownRegion Region = iota
Local
)
type Status int
const (
StatusReady Status= iota + 1
StatusDone
// บางทีเราอาจมี StatusInProgress ในอนาคต
)
func printInfo(name string, region Region, status Status)
ใช้ raw string literals เพื่อหลีกเลี่ยงการต้องถอด escape
Go รองรับการใช้ raw string literals ซึ่งประกอบด้วย " ` " เพื่อแทนสตริงแบบ raw ในสถานการณ์ที่ต้องการการถอด escape เราควรใช้วิธีนี้เพื่อแทนที่การต้องถอด escape สตริงที่ยากต่อการอ่านมากขึ้น
มันสามารถขยายหรือรวมตัวอักษรได้หลายบรรทัดและรวมถึงการใช้เครื่องหมายคำพูด การใช้สตริงเหล่านี้สามารถหลีกเลี่ยงสตริงที่ถอด escape ที่ยากต่อการอ่านมากขึ้น
ไม่แนะนำ:
wantError := "unknown name:\"test\""
แนะนำ:
wantError := `unknown error:"test"`
กำหนดค่าเริ่มต้นของ struct
กำหนดโครงสร้างเริ่มต้นโดยใช้ชื่อฟิลด์
เมื่อกำหนดโครงสร้าง ควรระบุชื่อฟิลด์เกือบทุกครั้ง ขณะนี้การดำเนินการนี้ได้รับการบังคับโดย go vet
ไม่แนะนำ:
k := User{"John", "Doe", true}
แนะนำ:
k := User{
FirstName: "John",
LastName: "Doe",
Admin: true,
}
ข้อยกเว้น: เมื่อมีฟิลด์ 3 หรือน้อยกว่า ชื่อฟิลด์ในตารางทดสอบ อาจ จะถูกข้าม
tests := []struct{
op Operation
want string
}{
{Add, "add"},
{Subtract, "subtract"},
}
ข้ามฟิลด์ค่าศูนย์ในโครงสร้าง
เมื่อกำหนดโครงสร้างด้วยฟิลด์ที่มีชื่อ ละลายไปถ้าไม่มีบริบทที่สำคัญ ไม่ต้องระบุฟิลด์ที่มีค่าเฉพาะศูนย์ กล่าวคือ ปล่อยให้เราตั้งค่าเหล่านี้เป็นค่าศูนย์โดยอัตโนมัติ
ไม่แนะนำ:
user := User{
FirstName: "John",
LastName: "Doe",
MiddleName: "",
Admin: false,
}
แนะนำ:
user := User{
FirstName: "John",
LastName: "Doe",
}
นี้ช่วยลดค่าที่ต้องอ่านด้วยการข้ามค่าตั้งต้นในบริบท เฉพาะระบุค่าที่สำคัญเท่านั้น
รวมถึงค่าศูนย์เมื่อชื่อฟิลด์มีบริบทที่สำคัญ ตัวอย่างเช่น กรณีทดสอบในตารางทดสอบจะได้ประโยชน์จากการตั้งชื่อฟิลด์ แม้ว่าเขาเป็นค่าศูนย์
tests := []struct{
give string
want int
}{
{give: "0", want: 0},
// ...
}
ใช้ var
สำหรับโครงสร้างค่าศูนย์
หากฟิลด์ทั้งหมดของโครงสร้างถูกข้ามในการประกาศ ใช้ var
เพื่อประกาศโครงสร้าง
ไม่แนะนำ:
user := User{}
แนะนำ:
var user User
นี้จะทำให้โครงสร้างค่าศูนย์แตกต่างจากค่าศูนย์ของฟิลด์มีค่าที่ไม่เท่ากับศูนย์ เหมือนกับที่เราชอบเมื่อ ประกาศ slice ที่ว่าง
เริ่มต้นตัวชี้ไปยังโครงสร้าง
เมื่อเริ่มต้นตัวชี้โครงสร้าง ใช้ &T{}
แทน new(T)
เพื่อให้มันสอดคล้องกับการเริ่มต้นโครงสร้าง
ไม่แนะนำ:
sval := T{Name: "foo"}
// ไม่สอดคล้อง
sptr := new(T)
sptr.Name = "bar"
แนะนำ:
sval := T{Name: "foo"}
sptr := &T{Name: "bar"}
เริ่มต้นแผนที่
สำหรับแผนที่ที่ว่าง ใช้ make(..)
เพื่อเริ่มต้น และแผนที่จะถูกเติมโปรแกรมโดยเทคนิคนี้ทำให้เริ่มดูแตกต่างจากการประกาศที่เหมือนกัน และมันยังช่วยให้เราเพิ่มข้อแนะนำขนาดหลังจากเริ่มด้วย
ไม่แนะนำ:
var (
// m1 มีความปลอดภัยในการเขียนอ่าน
// m2 โดนยุติเมื่อเขียน
m1 = map[T1]T2{}
m2 map[T1]T2
)
แนะนำ:
var (
// m1 มีความปลอดภัยในการเขียนอ่าน
// m2 โดนยุติเมื่อเขียน
m1 = make(map[T1]T2)
m2 map[T1]T2
)
ประกาศและเริ่มต้นดูเหมือนกันเป็นอย่างมาก | ประกาศและเริ่มต้นดูแตกต่างกัน |
เมื่อมีโอกาส ให้ระบุขนาดของแผนที่ในขณะเริ่มต้น ดูที่การระบุความจุแผนที่สำหรับรายละเอียดเพิ่มเติม
อีกทั้ง ถ้าแผนที่มีรายการคงที่ ใช้ map literals เพื่อเริ่มต้นแผนที่
ไม่แนะนำ:
m := make(map[T1]T2, 3)
m[k1] = v1
m[k2] = v2
m[k3] = v3
แนะนำ:
m := map[T1]T2{
k1: v1,
k2: v2,
k3: v3,
}
แนวทางพื้นฐานคือการใช้ map literals สำหรับการเพิ่มชุดค่าคงที่ระหว่างการเริ่มต้น หรือใช้ make
(และถ้าเป็นไปได้ ระบุความจุแผนที่)
รูปแบบสตริงสำหรับฟังก์ชันรูปแบบ Printf
หากคุณประกาศสตริงรูปแบบฟังก์ชัน Printf
นอกฟังก์ชัน กำหนดเป็นค่าคงที่ const
นี้ช่วยให้ go vet
ทำการวิเคราะห์สถิตแบบเอกสารในสตริงรูปแบบ
ไม่แนะนำ:
msg := "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)
แนะนำ:
const msg = "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)
การตั้งชื่อฟังก์ชันแบบ Printf
เมื่อประกาศฟังก์ชันแบบ Printf
ให้ตรวจสอบว่า go vet
สามารถตรวจจับและตรวจสอบสตริงรูปแบบได้
นั่นหมายความว่าคุณควรใช้ชื่อฟังก์ชันแบบ Printf
ที่กำหนดไว้ให้มากที่สุด go vet
จะตรวจสอบเหล่านี้โดยค่าเริ่มต้น สำหรับข้อมูลเพิ่มเติม ดูที่ Printf Family
หากไม่สามารถใช้ชื่อที่กำหนดไว้ได้ ให้จบชื่อที่เลือกไว้ด้วย f
: Wrapf
แทน Wrap
go vet
สามารถขอตรวจสอบชื่อแบบ Printf ที่กำหนดเฉพาะได้ แต่ชื่อจะต้องจบด้วย f
go vet -printfuncs=wrapf,statusf