Functions & Methods Basics
10 examples to get you started with Functions & Interfaces - 7 basic and 3 intermediate.
Search across all documentation pages
10 examples to get you started with Functions & Interfaces - 7 basic and 3 intermediate.
go version).Go functions return explicit values, often a result plus error.
package main
import (
"fmt"
"strconv"
)
func parsePort(s string) (int, error) {
return strconv.Atoi(s)
}
func main() {
port, err := parsePort("8080")
if err != nil {
fmt.Println("parse:", err)
return
}
fmt.Println("listening on", port)
}error at the call site - control flow stays visible._.Related: Multiple Returns, Named Returns & Naked Returns - when to name results
Methods attach behavior to types; value receivers operate on a copy.
package main
import "fmt"
type Counter struct {
n int
}
func (c Counter) Value() int {
return c.n
}
func main() {
c := Counter{n: 3}
fmt.Println(c.Value())
}(c Counter) copies the struct for the method body.Related: Methods: Value vs Pointer Receivers - choosing receiver kind
Pointer receivers mutate the original value and match larger structs efficiently.
package main
import "fmt"
type Counter struct {
n int
}
func (c *Counter) Inc() {
c.n++
}
func main() {
c := &Counter{}
c.Inc()
c.Inc()
fmt.Println(c.n)
}(c *Counter) lets Inc modify caller state.c.Inc() on a value variable.Interfaces are satisfied implicitly when methods match.
package main
import "fmt"
type Stringer interface {
String() string
}
type User struct {
Name string
}
func (u User) String() string {
return "user:" + u.Name
}
func printLabel(s Stringer) {
fmt.Println(s.String())
}
func main() {
printLabel(User{Name: "ada"})
}User never mentions Stringer - the compiler checks at assignment.Related: Defining and Implementing Interfaces - API design patterns
Functions are first-class values with their own named types.
package main
import "fmt"
type Op func(int, int) int
func apply(a, b int, op Op) int {
return op(a, b)
}
func main() {
add := func(x, y int) int { return x + y }
fmt.Println(apply(2, 3, add))
}type Op func(...) documents intent better than repeating func(int,int) int.Related: Variadic Functions & Function Types - variadic and HOF patterns
The final parameter can absorb a variable argument list.
package main
import "fmt"
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func main() {
fmt.Println(sum(1, 2, 3, 4))
}nums ...int is a slice inside the function ([]int).sum(vals...).Result names act as variables in the function body and appear in documentation.
package main
import "fmt"
func divide(a, b float64) (quot float64, ok bool) {
if b == 0 {
return 0, false
}
quot = a / b
ok = true
return
}
func main() {
q, ok := divide(10, 2)
fmt.Println(q, ok)
}return returns the current named values - use sparingly for clarity.Related: Multiple Returns, Named Returns & Naked Returns - naked return trade-offs
Closures capture variables by reference; loop iterators need a local copy per iteration.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
i := i
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println(i)
}()
}
wg.Wait()
}i := i, goroutines may all observe the final loop value.defer wg.Done() pairs with Add for clean shutdown of concurrent work.Related: Function Literals & Closures - factory and middleware patterns
Wire dependencies through small interfaces; constructors return concrete types.
package main
import (
"context"
"fmt"
)
type Notifier interface {
Notify(ctx context.Context, msg string) error
}
type Logger struct{}
func (Logger) Notify(ctx context.Context, msg string) error {
fmt.Println("notify:", msg)
return nil
}
type Service struct {
n Notifier
}
func NewService(n Notifier) *Service {
return &Service{n: n}
}
func (s *Service) Run(ctx context.Context) error {
return s.n.Notify(ctx, "ready")
}
func main() {
svc := NewService(Logger{})
_ = svc.Run(context.Background())
}NewService accepts Notifier for tests; returns *Service as concrete API.context.Context as the first parameter on I/O boundaries.Related: Functions and Interfaces: Go's Composition Model - mental model for composition
A nil pointer stored in an interface is not equal to an untyped nil interface.
package main
import "fmt"
type Worker interface {
Work() error
}
type job struct{}
func (j *job) Work() error { return nil }
func main() {
var w Worker = (*job)(nil)
fmt.Println(w == nil)
}w holds type *job with value nil - the interface itself is non-nil.if w == nil checks fail even though no concrete instance exists.Related: Nil Interface vs Nil Pointer - API designs that avoid the trap
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 18, 2026