// Good: Clear and directfuncGetUser(id string) (*User, error) {
user, err := db.FindUser(id)
if err != nil {
returnnil, fmt.Errorf("get user %s: %w", id, err)
}
return user, nil
}
// Bad: Overly cleverfuncGetUser(id string) (*User, error) {
returnfunc() (*User, error) {
if u, e := db.FindUser(id); e == {
u,
} {
, e
}
}()
}
nil
return
nil
else
return
nil
2. 让零值变得有用
设计类型时,应使其零值无需初始化即可立即使用。
// Good: Zero value is usefultype Counter struct {
mu sync.Mutex
count int// zero value is 0, ready to use
}
func(c *Counter) Inc() {
c.mu.Lock()
c.count++
c.mu.Unlock()
}
// Good: bytes.Buffer works with zero valuevar buf bytes.Buffer
buf.WriteString("hello")
// Bad: Requires initializationtype BadCounter struct {
counts map[string]int// nil map will panic
}
// In the consumer package, not the providerpackage service
// UserStore defines what this service needstype UserStore interface {
GetUser(id string) (*User, error)
SaveUser(user *User) error
}
type Service struct {
store UserStore
}
// Concrete implementation can be in another package// It doesn't need to know about this interface
使用类型断言实现可选行为
type Flusher interface {
Flush() error
}
funcWriteAndFlush(w io.Writer, data []byte)error {
if _, err := w.Write(data); err != nil {
return err
}
// Flush if supportedif f, ok := w.(Flusher); ok {
return f.Flush()
}
returnnil
}
// Bad: Creates many string allocationsfuncjoin(parts []string)string {
var result stringfor _, p := range parts {
result += p + ","
}
return result
}
// Good: Single allocation with strings.Builderfuncjoin(parts []string)string {
var sb strings.Builder
for i, p := range parts {
if i > 0 {
sb.WriteString(",")
}
sb.WriteString(p)
}
return sb.String()
}
// Best: Use standard libraryfuncjoin(parts []string)string {
return strings.Join(parts, ",")
}
Go 工具集成
基本命令
# Build and run
go build ./...
go run ./cmd/myapp
# Testing
go test ./...
go test -race ./...
go test -cover ./...
# Static analysis
go vet ./...
staticcheck ./...
golangci-lint run
# Module management
go mod tidy
go mod verify
# Formatting
gofmt -w .
goimports -w .
// Bad: Naked returns in long functionsfuncprocess() (result int, err error) {
// ... 50 lines ...return// What is being returned?
}
// Bad: Using panic for control flowfuncGetUser(id string) *User {
user, err := db.Find(id)
if err != nil {
panic(err) // Don't do this
}
return user
}
// Bad: Passing context in structtype Request struct {
ctx context.Context // Context should be first param
ID string
}
// Good: Context as first parameterfuncProcessRequest(ctx context.Context, id string)error {
// ...
}
// Bad: Mixing value and pointer receiverstype Counter struct{ n int }
func(c Counter) Value() int { return c.n } // Value receiverfunc(c *Counter) Increment() { c.n++ } // Pointer receiver// Pick one style and be consistent