หลีกเลี่ยงบรรทัดที่ยาวเกินไป

หลีกเลี่ยงการใช้บรรทัดโค้ดที่ทำให้ผู้อ่านต้องเลื่อนตำแหน่งในแนวนอนหรือหมุนเอกสารอย่างมาก

เราแนะนำให้ จำกัดความยาวของบรรทัดไว้ที่ 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