当前位置:网站首页>Go language programming specification combing summary

Go language programming specification combing summary

2022-07-05 15:27:00 dnice

This article summarizes 20 strip go Language programming experience , The reference link is at the end of the article , Interested friends can check .

1. Standardize and uniformly define package, Avoid circular dependencies

go Circular dependencies are not supported , So we're going to be here package Make more efforts in design , Avoid circular dependency in multi person collaborative development .

2. Avoid lines of code that are too long

The threshold of single line code can be uniformly Limited ,uber_go_guide It is suggested to limit the president to 99 Characters , But no rigid restrictions , This limit can be exceeded .

3. Similar statements are placed in a group

Go Language support puts similar statements in a group .

import "a"
import "b"

import (
  "a"
  "b"
)

This also applies to constants 、 Variable and type declaration :


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
)

Put only the relevant statements in a group . Don't put irrelevant statements in a group .

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"

There is no limit to where groups can be used , for example : You can use them inside functions :

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)
  )

  ...
}

4.import grouping

import Should be divided into two groups :

  • Standard library
  • Other libraries

import (
  "fmt"
  "os"
  "go.uber.org/atomic"
  "golang.org/x/sync/errgroup"
)

import (
  "fmt"
  "os"

  "go.uber.org/atomic"
  "golang.org/x/sync/errgroup"
)

5. Package name

When naming a package , Please select a name according to the following rules :

  • All lowercase . No capitals or underscores .
  • In most cases where named imports are used , No need to rename .
  • Short and concise .
  • Don't use the plural . for example net/url, instead of net/urls.
  • Do not use “common”,“util”,“shared” or “lib”. These are not good , Names with insufficient information .

6. Function name

follow Go Community Convention on using hump naming as function name . There is one exception , To group related test cases , Function names may contain underscores , Such as :TestMyFunction_WhatIsBeingTested.

7. Import alias

In two cases, you need to use the import alias , Otherwise, you should avoid using import aliases .

  • If the package name does not match the last element of the import path , You must use the import alias .
import (
  "net/http"

  client "example.com/client-go"
  trace "example.com/trace/v2"
)
  • When there is a direct conflict between importing multiple package names , Import alias should be used .
import (
  "fmt"
  "os"

  nettrace "golang.net/x/trace"
)

import (
  "fmt"
  "os"
  "runtime/trace"

  nettrace "golang.net/x/trace"
)

8. Function grouping and order

  • Functions should be sorted in rough order of call .
  • Functions in the same file should be grouped by recipients .

9. Reduce the nested

The code should deal with error situations as early as possible / Special case and return or continue loop as early as possible to reduce nesting . Reduce the amount of code nesting multiple levels of code .

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()
}

10. Reduce unnecessary else

If in if Variables are set in both branches of , You can replace it with a single if.

var a int
if b {
    
  a = 100
} else {
    
  a = 10
}

a := 10
if b {
    
  a = 100
}

11. Top level variable declaration

On the top floor , Use standards var keyword . Do not specify type , Unless it's different from the type of expression .

var _s string = F()

func F() string {
     return "A" }

var _s = F()
//  because  F  You've made it clear to return a string type , So we don't need to explicitly specify _s  The type of 
//  It's the same type 

func F() string {
     return "A" }

If the type of the expression does not exactly match the expected type , Please specify the type .

type myError struct{
    }

func (myError) Error() string {
     return "error" }

func F() myError {
     return myError{
    } }

var _e error = F()
// F  Return to one  myError  Instance of type , But we have to  error  type 

12. Local variable declaration

If the variable is explicitly set to a value , You should use the short variable declaration form (:=).

var s = "foo"

s := "foo"

however , In some cases ,var The default values are clearer when using keywords . for example , Declare empty slices .

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)
    }
  }
}

13.nil Is an effective slice

nil Is an effective length for 0 Of slice, It means :

  • Return should be used nil Instead of returning slices with zero length .
if x == "" {
    
  return []int{
    }
}

if x == "" {
    
  return nil
}
  • To check if the slice is empty , Please always use len(s) == 0. Instead of nil.
func isEmpty(s []string) bool {
    
  return s == nil
}

func isEmpty(s []string) bool {
    
  return len(s) == 0
}
  • Zero slice ( use var Slice of the declaration ) It can be used immediately , Don't need to call make() establish .
nums := []int{
    }
// or, 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)
}

Be careful : although nil Slice is a valid slice , But it is not equal to the length 0 The section of ( One for nil, The other is not ), And in different situations ( For example, serialization ), These two slices may be processed differently .

14. Avoid ambiguous parameter semantics

Ambiguous parameters in function calls can damage readability . When the meaning of the parameter name is not obvious , Please add C Style notes (/* … */)

// func printInfo(name string, isLocal, done bool)

printInfo("foo", true, true)

// func printInfo(name string, isLocal, done bool)

printInfo("foo", true /* isLocal */, true /* done */)

For the example code above , There is a better way to deal with it bool Type to custom type . future , This parameter can support more than two states (true/false).

type Region int

const (
  UnknownRegion Region = iota
  Local
)

type Status int

const (
  StatusReady Status= iota + 1
  StatusDone
  // Maybe we will have a StatusInProgress in the future.
)

func printInfo(name string, region Region, status Status)

15. Use the original string literal , Avoid escaping

Go Support use Original string literal , That is to say " ` " To represent a native string , In the situation where escape is needed , We should try to use this scheme to replace .

Can span multiple lines and include quotes . Using these strings can avoid manually escaping strings that are more difficult to read .

wantError := "unknown name:\"test\""

wantError := `unknown error:"test"`

16. Initialize structure

  • Initialize the structure with the field name
    When initializing structure , You should almost always specify a field name .
k := User{
    "John", "Doe", true}

k := User{
    
    FirstName: "John",
    LastName: "Doe",
    Admin: true,
}

exception : When there is 3 When there are two or fewer fields , The field names in the test table can be omitted .

tests := []struct{
    
  op Operation
  want string
}{
    
  {
    Add, "add"},
  {
    Subtract, "subtract"},
}
  • Omit the zero value field in the structure
    When initializing a structure with a field name , Unless a meaningful context is provided , Otherwise, fields with zero values are ignored . That is to say , Let's automatically set these to zero .
user := User{
    
  FirstName: "John",
  LastName: "Doe",
  MiddleName: "",
  Admin: false,
}

user := User{
    
  FirstName: "John",
  LastName: "Doe",
}

This helps reduce dyslexia by omitting default values in this context . Specify only meaningful values .

  • Use... For zero valued structures var
    If you omit all the fields of the structure in the declaration , Please use var Declaration structure .
user := User{
    }

var user User
  • initialization Struct quote
    When initializing a structure reference , Please use &T{} Instead of new(T), To make it consistent with struct initialization .
sval := T{
    Name: "foo"}

// inconsistent
sptr := new(T)
sptr.Name = "bar"

sval := T{
    Name: "foo"}

sptr := &T{
    Name: "bar"}

17. Specify the container capacity

Specify the container capacity as much as possible , To pre allocate memory for the container . This will minimize subsequent allocations when adding elements ( By copying and resizing containers ).

  • Appoint Map Capacity prompt
    As far as possible , In the use of make() Provide capacity information during initialization
make(map[T1]T2, hint)

towards make() Provide the capacity prompt and try to adjust it during initialization map Size , This will reduce the number of elements added to map When is map Reallocate memory .

Be careful , And slices Different .map capacity Prompt does not guarantee full preemptive allocation , It is used to estimate the required hashmap bucket The number of . therefore , Adding elements to map when , Even when specifying map Capacity time , Allocation may still occur .

m := make(map[string]os.FileInfo)

files, _ := ioutil.ReadDir("./files")
for _, f := range files {
    
    m[f.Name()] = f
}
// m  It was created without a size hint ;  There may be more assignments at run time .


files, _ := ioutil.ReadDir("./files")

m := make(map[string]os.FileInfo, len(files))
for _, f := range files {
    
    m[f.Name()] = f
}
// m  It's created with size hints ; There may be fewer assignments at run time .
  • Specify slice capacity

As far as possible , In the use of make() Provide capacity information when initializing slices , Especially when adding slices .

make([]T, length, capacity)

And maps Different ,slice capacity Not a hint : The compiler will provide to make() Of slice Allocate enough memory , This means subsequent append()` Operation will result in zero allocation ( until slice The length of the matches the capacity , After that , whatever append Can be resized to accommodate other elements ).

for n := 0; n < b.N; n++ {
    
  data := make([]int, 0)
  for k := 0; k < size; k++{
    
    data = append(data, k)
  }
}
BenchmarkBad-4    100000000    2.48s

for n := 0; n < b.N; n++ {
    
  data := make([]int, 0, size)
  for k := 0; k < size; k++{
    
    data = append(data, k)
  }
}
BenchmarkGood-4   100000000    0.21s

18. priority of use strconv instead of fmt

When converting primitives to or from strings ,strconv Velocity ratio fmt fast .

for i := 0; i < b.N; i++ {
    
  s := fmt.Sprint(rand.Int())
}
BenchmarkFmtSprint-4    143 ns/op    2 allocs/op

for i := 0; i < b.N; i++ {
    
  s := strconv.Itoa(rand.Int())
}
BenchmarkStrconv-4    64.2 ns/op    1 allocs/op

19. Avoid string to byte conversion

Do not repeatedly create bytes from fixed strings slice. contrary , Please perform a conversion and capture the results .

for i := 0; i < b.N; i++ {
    
  w.Write([]byte("Hello world"))
}
BenchmarkBad-4   50000000   22.2 ns/op

data := []byte("Hello world")
for i := 0; i < b.N; i++ {
    
  w.Write(data)
}
BenchmarkGood-4  500000000   3.25 ns/op

20. character string string format

If you declare outside the function Printf-style The format string of the function , Please set it to const Constant .

This helps go vet Perform static analysis of format strings .

msg := "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)

const msg = "unexpected values %v, %v\n"
fmt.Printf(msg, 1, 2)

Reference link

Reference links for this article :https://github.com/uber-go/guide/blob/master/style.md

原网站

版权声明
本文为[dnice]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/186/202207051506013591.html