117 lines
4.0 KiB
Markdown
117 lines
4.0 KiB
Markdown
# Functions, Methods & Options
|
|
|
|
## Functions and Methods
|
|
|
|
Functions returning a value are named like **nouns** (what they return). Functions performing actions are named like **verbs** (what they do).
|
|
|
|
```go
|
|
// Noun-like: returns something
|
|
func UserName() string { ... }
|
|
func DefaultConfig() Config { ... }
|
|
|
|
// Verb-like: performs an action
|
|
func WriteFile(name string, data []byte) error { ... }
|
|
func SendNotification(user *User) error { ... }
|
|
```
|
|
|
|
NEVER repeat the package name in function names:
|
|
|
|
```go
|
|
// Good: users call http.Get(), not http.HTTPGet()
|
|
package http
|
|
func Get(url string) (*Response, error)
|
|
|
|
// Bad: stutters at the call site
|
|
package http
|
|
func HTTPGet(url string) (*Response, error)
|
|
```
|
|
|
|
Functions that accept a format string and variadic args (like `fmt.Sprintf`) MUST end with **`f`**:
|
|
|
|
```go
|
|
// Good
|
|
func Errorf(format string, args ...any) error
|
|
func Wrapf(err error, format string, args ...any) error
|
|
func Logf(format string, args ...any)
|
|
|
|
// Bad
|
|
func Error(format string, args ...any) error // looks like it takes a plain string
|
|
func WrapError(err error, format string, args ...any) error
|
|
```
|
|
|
|
### Getters and Setters
|
|
|
|
Getters MUST NOT use the `Get` prefix. The getter is simply the field name, capitalized.
|
|
|
|
```go
|
|
// Good
|
|
func (u *User) Name() string { return u.name }
|
|
func (u *User) SetName(name string) { u.name = name }
|
|
|
|
// Bad
|
|
func (u *User) GetName() string { return u.name }
|
|
```
|
|
|
|
Only use `Get` when the underlying concept inherently uses "get" (e.g., HTTP GET). For expensive or blocking operations, use `Fetch` or `Compute` to signal that the call is not trivial.
|
|
|
|
**Exception — boolean predicates keep the `Is`/`Has`/`Can` prefix.** The no-Get rule applies to value getters, not boolean predicates. A method returning `bool` SHOULD use `Is`/`Has`/`Can` to read naturally as a question — this follows the standard library pattern (`reflect.Type.IsVariadic()`, `net.IP.IsLoopback()`, `big.Int.IsInt64()`).
|
|
|
|
```go
|
|
// Good — boolean predicate keeps Is prefix
|
|
func (s *Server) IsHealthy() bool { return s.healthy }
|
|
|
|
// Good — value getter omits Get prefix
|
|
func (s *Server) Port() int { return s.port }
|
|
|
|
// Good — richer return type, bare name is fine (different semantics)
|
|
func (s *Server) Healthy() (HealthStatus, error)
|
|
|
|
// Bad — bare adjective for bool is ambiguous
|
|
func (s *Server) Healthy() bool { return s.healthy }
|
|
|
|
// Bad — Get prefix on value getter is redundant
|
|
func (s *Server) GetPort() int { return s.port }
|
|
```
|
|
|
|
### Constructors
|
|
|
|
Name constructors `New` when the package exports a single primary type, or `NewTypeName` when there are multiple types.
|
|
|
|
```go
|
|
// Single primary type — New is unambiguous
|
|
package ring
|
|
func New(size int) *Ring
|
|
|
|
// Multiple types — qualify with the type name
|
|
package http
|
|
func NewRequest(method, url string, body io.Reader) (*Request, error)
|
|
func NewServeMux() *ServeMux
|
|
```
|
|
|
|
### Named Return Values
|
|
|
|
Named return values SHOULD only be used when it improves readability — typically when multiple return values have the same type, or when the names serve as documentation.
|
|
|
|
```go
|
|
// Good — names clarify which int64 is which
|
|
func Copy(dst Writer, src Reader) (written int64, err error)
|
|
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
|
|
|
|
// Good — single error return, no name needed
|
|
func Write(p []byte) (int, error)
|
|
|
|
// Bad — names add no clarity
|
|
func Read(p []byte) (bytes int, e error) // "bytes" shadows the package, "e" is non-standard
|
|
```
|
|
|
|
NEVER use named returns just to enable bare `return` — bare returns hurt readability in anything but the shortest functions.
|
|
|
|
## Functional Options Pattern
|
|
|
|
When a constructor has 3+ optional parameters that may grow, use the **functional options pattern** for clean, extensible APIs.
|
|
|
|
- **Struct**: `ServerOptions`, `ClientOptions` (not `Opts`, `Params`, `Settings`, `Config`)
|
|
- **Function type**: `ServerOption` (singular, not plural)
|
|
- **With\* functions**: `WithPort()`, `WithTimeout()`, `WithLogger()`
|
|
- **Factory**: `DefaultServerOptions()`
|