Add Golang skill and reference materials
- Introduced a new skill for Golang, providing comprehensive guidance on writing, debugging, and best practices for Go programming. - Added reference materials including effective Go guidelines, common patterns, and a quick reference cheat sheet to support users in Go development. - Created a skill creator guide to assist in developing new skills with structured templates and resource management. - Implemented scripts for skill initialization and packaging to streamline the skill creation process.
This commit is contained in:
649
.claude/skills/golang/references/common-patterns.md
Normal file
649
.claude/skills/golang/references/common-patterns.md
Normal file
@@ -0,0 +1,649 @@
|
||||
# Go Common Patterns and Idioms
|
||||
|
||||
## Design Patterns
|
||||
|
||||
### Functional Options Pattern
|
||||
|
||||
Used for configuring objects with many optional parameters:
|
||||
|
||||
```go
|
||||
type Server struct {
|
||||
host string
|
||||
port int
|
||||
timeout time.Duration
|
||||
maxConn int
|
||||
}
|
||||
|
||||
type Option func(*Server)
|
||||
|
||||
func WithHost(host string) Option {
|
||||
return func(s *Server) {
|
||||
s.host = host
|
||||
}
|
||||
}
|
||||
|
||||
func WithPort(port int) Option {
|
||||
return func(s *Server) {
|
||||
s.port = port
|
||||
}
|
||||
}
|
||||
|
||||
func WithTimeout(timeout time.Duration) Option {
|
||||
return func(s *Server) {
|
||||
s.timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func NewServer(opts ...Option) *Server {
|
||||
// Set defaults
|
||||
s := &Server{
|
||||
host: "localhost",
|
||||
port: 8080,
|
||||
timeout: 30 * time.Second,
|
||||
maxConn: 100,
|
||||
}
|
||||
|
||||
// Apply options
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Usage
|
||||
srv := NewServer(
|
||||
WithHost("example.com"),
|
||||
WithPort(443),
|
||||
WithTimeout(60 * time.Second),
|
||||
)
|
||||
```
|
||||
|
||||
### Builder Pattern
|
||||
|
||||
For complex object construction:
|
||||
|
||||
```go
|
||||
type HTTPRequest struct {
|
||||
method string
|
||||
url string
|
||||
headers map[string]string
|
||||
body []byte
|
||||
}
|
||||
|
||||
type RequestBuilder struct {
|
||||
request *HTTPRequest
|
||||
}
|
||||
|
||||
func NewRequestBuilder() *RequestBuilder {
|
||||
return &RequestBuilder{
|
||||
request: &HTTPRequest{
|
||||
headers: make(map[string]string),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *RequestBuilder) Method(method string) *RequestBuilder {
|
||||
b.request.method = method
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *RequestBuilder) URL(url string) *RequestBuilder {
|
||||
b.request.url = url
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *RequestBuilder) Header(key, value string) *RequestBuilder {
|
||||
b.request.headers[key] = value
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *RequestBuilder) Body(body []byte) *RequestBuilder {
|
||||
b.request.body = body
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *RequestBuilder) Build() *HTTPRequest {
|
||||
return b.request
|
||||
}
|
||||
|
||||
// Usage
|
||||
req := NewRequestBuilder().
|
||||
Method("POST").
|
||||
URL("https://api.example.com").
|
||||
Header("Content-Type", "application/json").
|
||||
Body([]byte(`{"key":"value"}`)).
|
||||
Build()
|
||||
```
|
||||
|
||||
### Singleton Pattern
|
||||
|
||||
Thread-safe singleton using sync.Once:
|
||||
|
||||
```go
|
||||
type Database struct {
|
||||
conn *sql.DB
|
||||
}
|
||||
|
||||
var (
|
||||
instance *Database
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func GetDatabase() *Database {
|
||||
once.Do(func() {
|
||||
conn, err := sql.Open("postgres", "connection-string")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
instance = &Database{conn: conn}
|
||||
})
|
||||
return instance
|
||||
}
|
||||
```
|
||||
|
||||
### Factory Pattern
|
||||
|
||||
```go
|
||||
type Animal interface {
|
||||
Speak() string
|
||||
}
|
||||
|
||||
type Dog struct{}
|
||||
func (d Dog) Speak() string { return "Woof!" }
|
||||
|
||||
type Cat struct{}
|
||||
func (c Cat) Speak() string { return "Meow!" }
|
||||
|
||||
type AnimalFactory struct{}
|
||||
|
||||
func (f *AnimalFactory) CreateAnimal(animalType string) Animal {
|
||||
switch animalType {
|
||||
case "dog":
|
||||
return &Dog{}
|
||||
case "cat":
|
||||
return &Cat{}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Strategy Pattern
|
||||
|
||||
```go
|
||||
type PaymentStrategy interface {
|
||||
Pay(amount float64) error
|
||||
}
|
||||
|
||||
type CreditCard struct {
|
||||
number string
|
||||
}
|
||||
|
||||
func (c *CreditCard) Pay(amount float64) error {
|
||||
fmt.Printf("Paying %.2f using credit card %s\n", amount, c.number)
|
||||
return nil
|
||||
}
|
||||
|
||||
type PayPal struct {
|
||||
email string
|
||||
}
|
||||
|
||||
func (p *PayPal) Pay(amount float64) error {
|
||||
fmt.Printf("Paying %.2f using PayPal account %s\n", amount, p.email)
|
||||
return nil
|
||||
}
|
||||
|
||||
type PaymentContext struct {
|
||||
strategy PaymentStrategy
|
||||
}
|
||||
|
||||
func (pc *PaymentContext) SetStrategy(strategy PaymentStrategy) {
|
||||
pc.strategy = strategy
|
||||
}
|
||||
|
||||
func (pc *PaymentContext) ExecutePayment(amount float64) error {
|
||||
return pc.strategy.Pay(amount)
|
||||
}
|
||||
```
|
||||
|
||||
## Concurrency Patterns
|
||||
|
||||
### Worker Pool
|
||||
|
||||
```go
|
||||
func worker(id int, jobs <-chan Job, results chan<- Result) {
|
||||
for job := range jobs {
|
||||
result := processJob(job)
|
||||
results <- result
|
||||
}
|
||||
}
|
||||
|
||||
func WorkerPool(numWorkers int, jobs []Job) []Result {
|
||||
jobsChan := make(chan Job, len(jobs))
|
||||
results := make(chan Result, len(jobs))
|
||||
|
||||
// Start workers
|
||||
for w := 1; w <= numWorkers; w++ {
|
||||
go worker(w, jobsChan, results)
|
||||
}
|
||||
|
||||
// Send jobs
|
||||
for _, job := range jobs {
|
||||
jobsChan <- job
|
||||
}
|
||||
close(jobsChan)
|
||||
|
||||
// Collect results
|
||||
var output []Result
|
||||
for range jobs {
|
||||
output = append(output, <-results)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
```
|
||||
|
||||
### Pipeline Pattern
|
||||
|
||||
```go
|
||||
func generator(nums ...int) <-chan int {
|
||||
out := make(chan int)
|
||||
go func() {
|
||||
for _, n := range nums {
|
||||
out <- n
|
||||
}
|
||||
close(out)
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
func square(in <-chan int) <-chan int {
|
||||
out := make(chan int)
|
||||
go func() {
|
||||
for n := range in {
|
||||
out <- n * n
|
||||
}
|
||||
close(out)
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create pipeline
|
||||
c := generator(2, 3, 4)
|
||||
out := square(c)
|
||||
|
||||
// Consume output
|
||||
for result := range out {
|
||||
fmt.Println(result)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fan-Out, Fan-In
|
||||
|
||||
```go
|
||||
func fanOut(in <-chan int, n int) []<-chan int {
|
||||
channels := make([]<-chan int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
channels[i] = worker(in)
|
||||
}
|
||||
return channels
|
||||
}
|
||||
|
||||
func worker(in <-chan int) <-chan int {
|
||||
out := make(chan int)
|
||||
go func() {
|
||||
for n := range in {
|
||||
out <- expensiveOperation(n)
|
||||
}
|
||||
close(out)
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
func fanIn(channels ...<-chan int) <-chan int {
|
||||
out := make(chan int)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(len(channels))
|
||||
for _, c := range channels {
|
||||
go func(ch <-chan int) {
|
||||
defer wg.Done()
|
||||
for n := range ch {
|
||||
out <- n
|
||||
}
|
||||
}(c)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(out)
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
```
|
||||
|
||||
### Timeout Pattern
|
||||
|
||||
```go
|
||||
func DoWithTimeout(timeout time.Duration) (result string, err error) {
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
result = expensiveOperation()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
return result, nil
|
||||
case <-time.After(timeout):
|
||||
return "", fmt.Errorf("operation timed out after %v", timeout)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Graceful Shutdown
|
||||
|
||||
```go
|
||||
func main() {
|
||||
server := &http.Server{Addr: ":8080"}
|
||||
|
||||
// Start server in goroutine
|
||||
go func() {
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("listen: %s\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for interrupt signal
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
log.Println("Shutting down server...")
|
||||
|
||||
// Graceful shutdown with timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := server.Shutdown(ctx); err != nil {
|
||||
log.Fatal("Server forced to shutdown:", err)
|
||||
}
|
||||
|
||||
log.Println("Server exiting")
|
||||
}
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
```go
|
||||
func rateLimiter(rate time.Duration) <-chan time.Time {
|
||||
return time.Tick(rate)
|
||||
}
|
||||
|
||||
func main() {
|
||||
limiter := rateLimiter(200 * time.Millisecond)
|
||||
|
||||
for req := range requests {
|
||||
<-limiter // Wait for rate limiter
|
||||
go handleRequest(req)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Circuit Breaker
|
||||
|
||||
```go
|
||||
type CircuitBreaker struct {
|
||||
maxFailures int
|
||||
timeout time.Duration
|
||||
failures int
|
||||
lastFail time.Time
|
||||
state string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (cb *CircuitBreaker) Call(fn func() error) error {
|
||||
cb.mu.Lock()
|
||||
defer cb.mu.Unlock()
|
||||
|
||||
if cb.state == "open" {
|
||||
if time.Since(cb.lastFail) > cb.timeout {
|
||||
cb.state = "half-open"
|
||||
} else {
|
||||
return fmt.Errorf("circuit breaker is open")
|
||||
}
|
||||
}
|
||||
|
||||
err := fn()
|
||||
if err != nil {
|
||||
cb.failures++
|
||||
cb.lastFail = time.Now()
|
||||
if cb.failures >= cb.maxFailures {
|
||||
cb.state = "open"
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
cb.failures = 0
|
||||
cb.state = "closed"
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling Patterns
|
||||
|
||||
### Error Wrapping
|
||||
|
||||
```go
|
||||
func processFile(filename string) (err error) {
|
||||
data, err := readFile(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process file %s: %w", filename, err)
|
||||
}
|
||||
|
||||
if err := validate(data); err != nil {
|
||||
return fmt.Errorf("validation failed for %s: %w", filename, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Sentinel Errors
|
||||
|
||||
```go
|
||||
var (
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrUnauthorized = errors.New("unauthorized")
|
||||
ErrInvalidInput = errors.New("invalid input")
|
||||
)
|
||||
|
||||
func FindUser(id int) (*User, error) {
|
||||
user, exists := users[id]
|
||||
if !exists {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// Check error
|
||||
user, err := FindUser(123)
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
// Handle not found
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Error Types
|
||||
|
||||
```go
|
||||
type ValidationError struct {
|
||||
Field string
|
||||
Value interface{}
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *ValidationError) Error() string {
|
||||
return fmt.Sprintf("validation failed for field %s with value %v: %v",
|
||||
e.Field, e.Value, e.Err)
|
||||
}
|
||||
|
||||
func (e *ValidationError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// Usage
|
||||
var validErr *ValidationError
|
||||
if errors.As(err, &validErr) {
|
||||
fmt.Printf("Field: %s\n", validErr.Field)
|
||||
}
|
||||
```
|
||||
|
||||
## Resource Management Patterns
|
||||
|
||||
### Defer for Cleanup
|
||||
|
||||
```go
|
||||
func processFile(filename string) error {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Process file
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Context for Cancellation
|
||||
|
||||
```go
|
||||
func fetchData(ctx context.Context, url string) ([]byte, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
```
|
||||
|
||||
### Sync.Pool for Object Reuse
|
||||
|
||||
```go
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
func process() {
|
||||
buf := bufferPool.Get().(*bytes.Buffer)
|
||||
defer bufferPool.Put(buf)
|
||||
|
||||
buf.Reset()
|
||||
// Use buffer
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
### Table-Driven Tests
|
||||
|
||||
```go
|
||||
func TestAdd(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a, b int
|
||||
expected int
|
||||
}{
|
||||
{"positive numbers", 2, 3, 5},
|
||||
{"negative numbers", -1, -1, -2},
|
||||
{"mixed signs", -5, 10, 5},
|
||||
{"zeros", 0, 0, 0},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := Add(tt.a, tt.b)
|
||||
if result != tt.expected {
|
||||
t.Errorf("Add(%d, %d) = %d; want %d",
|
||||
tt.a, tt.b, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Mock Interfaces
|
||||
|
||||
```go
|
||||
type Database interface {
|
||||
Get(key string) (string, error)
|
||||
Set(key, value string) error
|
||||
}
|
||||
|
||||
type MockDB struct {
|
||||
data map[string]string
|
||||
}
|
||||
|
||||
func (m *MockDB) Get(key string) (string, error) {
|
||||
val, ok := m.data[key]
|
||||
if !ok {
|
||||
return "", errors.New("not found")
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (m *MockDB) Set(key, value string) error {
|
||||
m.data[key] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestUserService(t *testing.T) {
|
||||
mockDB := &MockDB{data: make(map[string]string)}
|
||||
service := NewUserService(mockDB)
|
||||
// Test service
|
||||
}
|
||||
```
|
||||
|
||||
### Test Fixtures
|
||||
|
||||
```go
|
||||
func setupTestDB(t *testing.T) (*sql.DB, func()) {
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Setup schema
|
||||
_, err = db.Exec(schema)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
db.Close()
|
||||
}
|
||||
|
||||
return db, cleanup
|
||||
}
|
||||
|
||||
func TestDatabase(t *testing.T) {
|
||||
db, cleanup := setupTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
// Run tests
|
||||
}
|
||||
```
|
||||
|
||||
423
.claude/skills/golang/references/effective-go-summary.md
Normal file
423
.claude/skills/golang/references/effective-go-summary.md
Normal file
@@ -0,0 +1,423 @@
|
||||
# Effective Go - Key Points Summary
|
||||
|
||||
Source: https://go.dev/doc/effective_go
|
||||
|
||||
## Formatting
|
||||
|
||||
- Use `gofmt` to automatically format your code
|
||||
- Indentation: use tabs
|
||||
- Line length: no strict limit, but keep reasonable
|
||||
- Parentheses: Go uses fewer parentheses than C/Java
|
||||
|
||||
## Commentary
|
||||
|
||||
- Every package should have a package comment
|
||||
- Every exported name should have a doc comment
|
||||
- Comments should be complete sentences
|
||||
- Start comments with the name of the element being described
|
||||
|
||||
Example:
|
||||
```go
|
||||
// Package regexp implements regular expression search.
|
||||
package regexp
|
||||
|
||||
// Compile parses a regular expression and returns, if successful,
|
||||
// a Regexp object that can be used to match against text.
|
||||
func Compile(str string) (*Regexp, error) {
|
||||
```
|
||||
|
||||
## Names
|
||||
|
||||
### Package Names
|
||||
- Short, concise, evocative
|
||||
- Lowercase, single-word
|
||||
- No underscores or mixedCaps
|
||||
- Avoid stuttering (e.g., `bytes.Buffer` not `bytes.ByteBuffer`)
|
||||
|
||||
### Getters/Setters
|
||||
- Getter: `Owner()` not `GetOwner()`
|
||||
- Setter: `SetOwner()`
|
||||
|
||||
### Interface Names
|
||||
- One-method interfaces use method name + -er suffix
|
||||
- Examples: `Reader`, `Writer`, `Formatter`, `CloseNotifier`
|
||||
|
||||
### MixedCaps
|
||||
- Use `MixedCaps` or `mixedCaps` rather than underscores
|
||||
|
||||
## Semicolons
|
||||
|
||||
- Lexer automatically inserts semicolons
|
||||
- Never put opening brace on its own line
|
||||
|
||||
## Control Structures
|
||||
|
||||
### If
|
||||
```go
|
||||
if err := file.Chmod(0664); err != nil {
|
||||
log.Print(err)
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
### Redeclaration
|
||||
```go
|
||||
f, err := os.Open(name)
|
||||
// err is declared here
|
||||
|
||||
d, err := f.Stat()
|
||||
// err is redeclared here (same scope)
|
||||
```
|
||||
|
||||
### For
|
||||
```go
|
||||
// Like a C for
|
||||
for init; condition; post { }
|
||||
|
||||
// Like a C while
|
||||
for condition { }
|
||||
|
||||
// Like a C for(;;)
|
||||
for { }
|
||||
|
||||
// Range over array/slice/map/channel
|
||||
for key, value := range oldMap {
|
||||
newMap[key] = value
|
||||
}
|
||||
|
||||
// If you only need the key
|
||||
for key := range m {
|
||||
// ...
|
||||
}
|
||||
|
||||
// If you only need the value
|
||||
for _, value := range array {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Switch
|
||||
- No automatic fall through
|
||||
- Cases can be expressions
|
||||
- Can switch on no value (acts like if-else chain)
|
||||
|
||||
```go
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
return c - '0'
|
||||
case 'a' <= c && c <= 'f':
|
||||
return c - 'a' + 10
|
||||
case 'A' <= c && c <= 'F':
|
||||
return c - 'A' + 10
|
||||
}
|
||||
```
|
||||
|
||||
### Type Switch
|
||||
```go
|
||||
switch t := value.(type) {
|
||||
case int:
|
||||
fmt.Printf("int: %d\n", t)
|
||||
case string:
|
||||
fmt.Printf("string: %s\n", t)
|
||||
default:
|
||||
fmt.Printf("unexpected type %T\n", t)
|
||||
}
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
### Multiple Return Values
|
||||
```go
|
||||
func (file *File) Write(b []byte) (n int, err error) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Named Result Parameters
|
||||
- Named results are initialized to zero values
|
||||
- Can be used for documentation
|
||||
- Enable naked returns
|
||||
|
||||
```go
|
||||
func ReadFull(r Reader, buf []byte) (n int, err error) {
|
||||
for len(buf) > 0 && err == nil {
|
||||
var nr int
|
||||
nr, err = r.Read(buf)
|
||||
n += nr
|
||||
buf = buf[nr:]
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
### Defer
|
||||
- Schedules function call to run after surrounding function returns
|
||||
- LIFO order
|
||||
- Arguments evaluated when defer executes
|
||||
|
||||
```go
|
||||
func trace(s string) string {
|
||||
fmt.Println("entering:", s)
|
||||
return s
|
||||
}
|
||||
|
||||
func un(s string) {
|
||||
fmt.Println("leaving:", s)
|
||||
}
|
||||
|
||||
func a() {
|
||||
defer un(trace("a"))
|
||||
fmt.Println("in a")
|
||||
}
|
||||
```
|
||||
|
||||
## Data
|
||||
|
||||
### Allocation with new
|
||||
- `new(T)` allocates zeroed storage for new item of type T
|
||||
- Returns `*T`
|
||||
- Returns memory address of newly allocated zero value
|
||||
|
||||
```go
|
||||
p := new(int) // p is *int, points to zeroed int
|
||||
```
|
||||
|
||||
### Constructors and Composite Literals
|
||||
```go
|
||||
func NewFile(fd int, name string) *File {
|
||||
if fd < 0 {
|
||||
return nil
|
||||
}
|
||||
return &File{fd: fd, name: name}
|
||||
}
|
||||
```
|
||||
|
||||
### Allocation with make
|
||||
- `make(T, args)` creates slices, maps, and channels only
|
||||
- Returns initialized (not zeroed) value of type T (not *T)
|
||||
|
||||
```go
|
||||
make([]int, 10, 100) // slice: len=10, cap=100
|
||||
make(map[string]int) // map
|
||||
make(chan int, 10) // buffered channel
|
||||
```
|
||||
|
||||
### Arrays
|
||||
- Arrays are values, not pointers
|
||||
- Passing array to function copies the entire array
|
||||
- Array size is part of its type
|
||||
|
||||
### Slices
|
||||
- Hold references to underlying array
|
||||
- Can grow dynamically with `append`
|
||||
- Passing slice passes reference
|
||||
|
||||
### Maps
|
||||
- Hold references to underlying data structure
|
||||
- Passing map passes reference
|
||||
- Zero value is `nil`
|
||||
|
||||
### Printing
|
||||
- `%v` - default format
|
||||
- `%+v` - struct with field names
|
||||
- `%#v` - Go syntax representation
|
||||
- `%T` - type
|
||||
- `%q` - quoted string
|
||||
|
||||
## Initialization
|
||||
|
||||
### Constants
|
||||
- Created at compile time
|
||||
- Can only be numbers, characters, strings, or booleans
|
||||
|
||||
### init Function
|
||||
- Each source file can have `init()` function
|
||||
- Called after package-level variables initialized
|
||||
- Used for setup that can't be expressed as declarations
|
||||
|
||||
```go
|
||||
func init() {
|
||||
// initialization code
|
||||
}
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
### Pointers vs. Values
|
||||
- Value methods can be invoked on pointers and values
|
||||
- Pointer methods can only be invoked on pointers
|
||||
|
||||
Rule: Value methods can be called on both values and pointers, but pointer methods should only be called on pointers (though Go allows calling on addressable values).
|
||||
|
||||
```go
|
||||
type ByteSlice []byte
|
||||
|
||||
func (slice ByteSlice) Append(data []byte) []byte {
|
||||
// ...
|
||||
}
|
||||
|
||||
func (p *ByteSlice) Append(data []byte) {
|
||||
slice := *p
|
||||
// ...
|
||||
*p = slice
|
||||
}
|
||||
```
|
||||
|
||||
## Interfaces and Other Types
|
||||
|
||||
### Interfaces
|
||||
- A type implements an interface by implementing its methods
|
||||
- No explicit declaration of intent
|
||||
|
||||
### Type Assertions
|
||||
```go
|
||||
value, ok := str.(string)
|
||||
```
|
||||
|
||||
### Type Switches
|
||||
```go
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
// v is string
|
||||
case int:
|
||||
// v is int
|
||||
}
|
||||
```
|
||||
|
||||
### Generality
|
||||
- If a type exists only to implement an interface and will never have exported methods beyond that interface, there's no need to export the type itself
|
||||
|
||||
## The Blank Identifier
|
||||
|
||||
### Unused Imports and Variables
|
||||
```go
|
||||
import _ "net/http/pprof" // Import for side effects
|
||||
```
|
||||
|
||||
### Interface Checks
|
||||
```go
|
||||
var _ json.Marshaler = (*RawMessage)(nil)
|
||||
```
|
||||
|
||||
## Embedding
|
||||
|
||||
### Composition, not Inheritance
|
||||
```go
|
||||
type ReadWriter struct {
|
||||
*Reader // *bufio.Reader
|
||||
*Writer // *bufio.Writer
|
||||
}
|
||||
```
|
||||
|
||||
## Concurrency
|
||||
|
||||
### Share by Communicating
|
||||
- Don't communicate by sharing memory; share memory by communicating
|
||||
- Use channels to pass ownership
|
||||
|
||||
### Goroutines
|
||||
- Cheap: small initial stack
|
||||
- Multiplexed onto OS threads
|
||||
- Prefix function call with `go` keyword
|
||||
|
||||
### Channels
|
||||
- Allocate with `make`
|
||||
- Unbuffered: synchronous
|
||||
- Buffered: asynchronous up to buffer size
|
||||
|
||||
```go
|
||||
ci := make(chan int) // unbuffered
|
||||
cj := make(chan int, 0) // unbuffered
|
||||
cs := make(chan *os.File, 100) // buffered
|
||||
```
|
||||
|
||||
### Channels of Channels
|
||||
```go
|
||||
type Request struct {
|
||||
args []int
|
||||
f func([]int) int
|
||||
resultChan chan int
|
||||
}
|
||||
```
|
||||
|
||||
### Parallelization
|
||||
```go
|
||||
const numCPU = runtime.NumCPU()
|
||||
runtime.GOMAXPROCS(numCPU)
|
||||
```
|
||||
|
||||
## Errors
|
||||
|
||||
### Error Type
|
||||
```go
|
||||
type error interface {
|
||||
Error() string
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Errors
|
||||
```go
|
||||
type PathError struct {
|
||||
Op string
|
||||
Path string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *PathError) Error() string {
|
||||
return e.Op + " " + e.Path + ": " + e.Err.Error()
|
||||
}
|
||||
```
|
||||
|
||||
### Panic
|
||||
- Use for unrecoverable errors
|
||||
- Generally avoid in library code
|
||||
|
||||
### Recover
|
||||
- Called inside deferred function
|
||||
- Stops panic sequence
|
||||
- Returns value passed to panic
|
||||
|
||||
```go
|
||||
func server(workChan <-chan *Work) {
|
||||
for work := range workChan {
|
||||
go safelyDo(work)
|
||||
}
|
||||
}
|
||||
|
||||
func safelyDo(work *Work) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println("work failed:", err)
|
||||
}
|
||||
}()
|
||||
do(work)
|
||||
}
|
||||
```
|
||||
|
||||
## A Web Server Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Counter struct {
|
||||
n int
|
||||
}
|
||||
|
||||
func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
ctr.n++
|
||||
fmt.Fprintf(w, "counter = %d\n", ctr.n)
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctr := new(Counter)
|
||||
http.Handle("/counter", ctr)
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
```
|
||||
|
||||
528
.claude/skills/golang/references/quick-reference.md
Normal file
528
.claude/skills/golang/references/quick-reference.md
Normal file
@@ -0,0 +1,528 @@
|
||||
# Go Quick Reference Cheat Sheet
|
||||
|
||||
## Basic Syntax
|
||||
|
||||
### Hello World
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, World!")
|
||||
}
|
||||
```
|
||||
|
||||
### Variables
|
||||
```go
|
||||
var name string = "John"
|
||||
var age int = 30
|
||||
var height = 5.9 // type inference
|
||||
|
||||
// Short declaration (inside functions only)
|
||||
count := 42
|
||||
```
|
||||
|
||||
### Constants
|
||||
```go
|
||||
const Pi = 3.14159
|
||||
const (
|
||||
Sunday = iota // 0
|
||||
Monday // 1
|
||||
Tuesday // 2
|
||||
)
|
||||
```
|
||||
|
||||
## Data Types
|
||||
|
||||
### Basic Types
|
||||
```go
|
||||
bool // true, false
|
||||
string // "hello"
|
||||
int int8 int16 int32 int64
|
||||
uint uint8 uint16 uint32 uint64
|
||||
byte // alias for uint8
|
||||
rune // alias for int32 (Unicode)
|
||||
float32 float64
|
||||
complex64 complex128
|
||||
```
|
||||
|
||||
### Composite Types
|
||||
```go
|
||||
// Array (fixed size)
|
||||
var arr [5]int
|
||||
|
||||
// Slice (dynamic)
|
||||
slice := []int{1, 2, 3}
|
||||
slice = append(slice, 4)
|
||||
|
||||
// Map
|
||||
m := make(map[string]int)
|
||||
m["key"] = 42
|
||||
|
||||
// Struct
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
p := Person{Name: "Alice", Age: 30}
|
||||
|
||||
// Pointer
|
||||
ptr := &p
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
```go
|
||||
// Basic function
|
||||
func add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
// Named returns (preferred)
|
||||
func divide(a, b float64) (result float64, err error) {
|
||||
if b == 0 {
|
||||
err = errors.New("division by zero")
|
||||
return
|
||||
}
|
||||
result = a / b
|
||||
return
|
||||
}
|
||||
|
||||
// Variadic
|
||||
func sum(nums ...int) int {
|
||||
total := 0
|
||||
for _, n := range nums {
|
||||
total += n
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// Multiple returns
|
||||
func swap(a, b int) (int, int) {
|
||||
return b, a
|
||||
}
|
||||
```
|
||||
|
||||
## Control Flow
|
||||
|
||||
### If/Else
|
||||
```go
|
||||
if x > 0 {
|
||||
// positive
|
||||
} else if x < 0 {
|
||||
// negative
|
||||
} else {
|
||||
// zero
|
||||
}
|
||||
|
||||
// With initialization
|
||||
if err := doSomething(); err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
### For Loops
|
||||
```go
|
||||
// Traditional for
|
||||
for i := 0; i < 10; i++ {
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
// While-style
|
||||
for condition {
|
||||
}
|
||||
|
||||
// Infinite
|
||||
for {
|
||||
}
|
||||
|
||||
// Range
|
||||
for i, v := range slice {
|
||||
fmt.Printf("%d: %v\n", i, v)
|
||||
}
|
||||
|
||||
for key, value := range myMap {
|
||||
fmt.Printf("%s: %v\n", key, value)
|
||||
}
|
||||
```
|
||||
|
||||
### Switch
|
||||
```go
|
||||
switch x {
|
||||
case 1:
|
||||
fmt.Println("one")
|
||||
case 2, 3:
|
||||
fmt.Println("two or three")
|
||||
default:
|
||||
fmt.Println("other")
|
||||
}
|
||||
|
||||
// Type switch
|
||||
switch v := i.(type) {
|
||||
case int:
|
||||
fmt.Printf("int: %d\n", v)
|
||||
case string:
|
||||
fmt.Printf("string: %s\n", v)
|
||||
}
|
||||
```
|
||||
|
||||
## Methods & Interfaces
|
||||
|
||||
### Methods
|
||||
```go
|
||||
type Rectangle struct {
|
||||
Width, Height float64
|
||||
}
|
||||
|
||||
// Value receiver
|
||||
func (r Rectangle) Area() float64 {
|
||||
return r.Width * r.Height
|
||||
}
|
||||
|
||||
// Pointer receiver
|
||||
func (r *Rectangle) Scale(factor float64) {
|
||||
r.Width *= factor
|
||||
r.Height *= factor
|
||||
}
|
||||
```
|
||||
|
||||
### Interfaces
|
||||
```go
|
||||
type Shape interface {
|
||||
Area() float64
|
||||
Perimeter() float64
|
||||
}
|
||||
|
||||
// Empty interface (any type)
|
||||
var x interface{} // or: var x any
|
||||
```
|
||||
|
||||
## Concurrency
|
||||
|
||||
### Goroutines
|
||||
```go
|
||||
go doSomething()
|
||||
|
||||
go func() {
|
||||
fmt.Println("In goroutine")
|
||||
}()
|
||||
```
|
||||
|
||||
### Channels
|
||||
```go
|
||||
// Create
|
||||
ch := make(chan int) // unbuffered
|
||||
ch := make(chan int, 10) // buffered
|
||||
|
||||
// Send & Receive
|
||||
ch <- 42 // send
|
||||
value := <-ch // receive
|
||||
|
||||
// Close
|
||||
close(ch)
|
||||
|
||||
// Check if closed
|
||||
value, ok := <-ch
|
||||
```
|
||||
|
||||
### Select
|
||||
```go
|
||||
select {
|
||||
case msg := <-ch1:
|
||||
fmt.Println("ch1:", msg)
|
||||
case msg := <-ch2:
|
||||
fmt.Println("ch2:", msg)
|
||||
case <-time.After(1 * time.Second):
|
||||
fmt.Println("timeout")
|
||||
default:
|
||||
fmt.Println("no channel ready")
|
||||
}
|
||||
```
|
||||
|
||||
### Sync Package
|
||||
```go
|
||||
// Mutex
|
||||
var mu sync.Mutex
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
// RWMutex
|
||||
var mu sync.RWMutex
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
|
||||
// WaitGroup
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// work
|
||||
}()
|
||||
wg.Wait()
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```go
|
||||
// Create errors
|
||||
err := errors.New("error message")
|
||||
err := fmt.Errorf("failed: %w", originalErr)
|
||||
|
||||
// Check errors
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Custom error type
|
||||
type MyError struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e *MyError) Error() string {
|
||||
return e.Msg
|
||||
}
|
||||
|
||||
// Error checking (Go 1.13+)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// handle
|
||||
}
|
||||
|
||||
var pathErr *os.PathError
|
||||
if errors.As(err, &pathErr) {
|
||||
// handle
|
||||
}
|
||||
```
|
||||
|
||||
## Standard Library Snippets
|
||||
|
||||
### fmt - Formatting
|
||||
```go
|
||||
fmt.Print("text")
|
||||
fmt.Println("text with newline")
|
||||
fmt.Printf("Name: %s, Age: %d\n", name, age)
|
||||
s := fmt.Sprintf("formatted %v", value)
|
||||
```
|
||||
|
||||
### strings
|
||||
```go
|
||||
strings.Contains(s, substr)
|
||||
strings.HasPrefix(s, prefix)
|
||||
strings.Join([]string{"a", "b"}, ",")
|
||||
strings.Split(s, ",")
|
||||
strings.ToLower(s)
|
||||
strings.TrimSpace(s)
|
||||
```
|
||||
|
||||
### strconv
|
||||
```go
|
||||
i, _ := strconv.Atoi("42")
|
||||
s := strconv.Itoa(42)
|
||||
f, _ := strconv.ParseFloat("3.14", 64)
|
||||
```
|
||||
|
||||
### io
|
||||
```go
|
||||
io.Copy(dst, src)
|
||||
data, _ := io.ReadAll(r)
|
||||
io.WriteString(w, "data")
|
||||
```
|
||||
|
||||
### os
|
||||
```go
|
||||
file, _ := os.Open("file.txt")
|
||||
defer file.Close()
|
||||
os.Getenv("PATH")
|
||||
os.Exit(1)
|
||||
```
|
||||
|
||||
### net/http
|
||||
```go
|
||||
// Server
|
||||
http.HandleFunc("/", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
|
||||
// Client
|
||||
resp, _ := http.Get("https://example.com")
|
||||
defer resp.Body.Close()
|
||||
```
|
||||
|
||||
### encoding/json
|
||||
```go
|
||||
// Encode
|
||||
data, _ := json.Marshal(obj)
|
||||
|
||||
// Decode
|
||||
json.Unmarshal(data, &obj)
|
||||
```
|
||||
|
||||
### time
|
||||
```go
|
||||
now := time.Now()
|
||||
time.Sleep(5 * time.Second)
|
||||
t.Format("2006-01-02 15:04:05")
|
||||
time.Parse("2006-01-02", "2024-01-01")
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Basic Test
|
||||
```go
|
||||
// mycode_test.go
|
||||
package mypackage
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
result := Add(2, 3)
|
||||
if result != 5 {
|
||||
t.Errorf("got %d, want 5", result)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Table-Driven Test
|
||||
```go
|
||||
func TestAdd(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a, b int
|
||||
expected int
|
||||
}{
|
||||
{"positive", 2, 3, 5},
|
||||
{"negative", -1, -1, -2},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := Add(tt.a, tt.b)
|
||||
if result != tt.expected {
|
||||
t.Errorf("got %d, want %d", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Benchmark
|
||||
```go
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Add(2, 3)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Go Commands
|
||||
|
||||
```bash
|
||||
# Run
|
||||
go run main.go
|
||||
|
||||
# Build
|
||||
go build
|
||||
go build -o myapp
|
||||
|
||||
# Test
|
||||
go test
|
||||
go test -v
|
||||
go test -cover
|
||||
go test -race
|
||||
|
||||
# Format
|
||||
go fmt ./...
|
||||
gofmt -s -w .
|
||||
|
||||
# Lint
|
||||
go vet ./...
|
||||
|
||||
# Modules
|
||||
go mod init module-name
|
||||
go mod tidy
|
||||
go get package@version
|
||||
go get -u ./...
|
||||
|
||||
# Install
|
||||
go install
|
||||
|
||||
# Documentation
|
||||
go doc package.Function
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Defer
|
||||
```go
|
||||
file, err := os.Open("file.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
```
|
||||
|
||||
### Error Wrapping
|
||||
```go
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to process: %w", err)
|
||||
}
|
||||
```
|
||||
|
||||
### Context
|
||||
```go
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
```
|
||||
|
||||
### Options Pattern
|
||||
```go
|
||||
type Option func(*Config)
|
||||
|
||||
func WithPort(port int) Option {
|
||||
return func(c *Config) {
|
||||
c.port = port
|
||||
}
|
||||
}
|
||||
|
||||
func New(opts ...Option) *Server {
|
||||
cfg := &Config{port: 8080}
|
||||
for _, opt := range opts {
|
||||
opt(cfg)
|
||||
}
|
||||
return &Server{cfg: cfg}
|
||||
}
|
||||
```
|
||||
|
||||
## Format Verbs
|
||||
|
||||
```go
|
||||
%v // default format
|
||||
%+v // struct with field names
|
||||
%#v // Go-syntax representation
|
||||
%T // type
|
||||
%t // bool
|
||||
%d // decimal integer
|
||||
%b // binary
|
||||
%o // octal
|
||||
%x // hex (lowercase)
|
||||
%X // hex (uppercase)
|
||||
%f // float
|
||||
%e // scientific notation
|
||||
%s // string
|
||||
%q // quoted string
|
||||
%p // pointer address
|
||||
%w // error wrapping
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Use `gofmt` to format code
|
||||
2. Always check errors
|
||||
3. Use named return values
|
||||
4. Prefer composition over inheritance
|
||||
5. Use defer for cleanup
|
||||
6. Keep functions small and focused
|
||||
7. Write table-driven tests
|
||||
8. Document exported names
|
||||
9. Use interfaces for flexibility
|
||||
10. Follow Effective Go guidelines
|
||||
|
||||
Reference in New Issue
Block a user