A function's signature fixes its parameter and return types; the compiler checks every call against it. Go permits multiple return values, named returns, and variadic parameters, patterns used constantly throughout the standard library.
A function has a name, its parameters, and what it returns. Any of those can be empty: plenty of useful functions take nothing, and plenty return nothing.
Here is one that converts Fahrenheit to Celsius. It takes a float64, returns a float64, and the body is a single expression.
func celsius(f float64) float64 {
return (f - 32) * 5 / 9
}
fmt.Println(celsius(212))Leave the name off and you have a value. It goes in a variable, gets passed to another function, or gets called on the spot, like any other value.
shorten := func(s string, max int) string {
if len(s) <= max {
return s
}
return s[:max] + "..."
}
fmt.Println(shorten("howtogo.dev/functions", 11))... on a parameter takes any number of arguments and hands you a slice. ... on a slice at the call site does the reverse, spreading its elements back out as those arguments.
Both calls run the same function. One passes three and four directly, the other passes a slice that happens to hold three numbers.
func total(counts ...int) int {
sum := 0
for _, n := range counts {
sum += n
}
return sum
}
fmt.Println(total(3, 4))
weekly := []int{12, 8, 15}
fmt.Println(total(weekly...))Four calls, three shapes of function: a named one, a literal held in a variable, and a variadic one invoked both ways.
$ go run functions.go
100
howtogo.dev...
7
35More examples
Every parameter needs an explicit type. Consecutive parameters that share a type can drop it from all but the last.
func add(x, y int) int {
return x + y
}
fmt.Println(add(2, 3))5func(...) T is a type like any other, so a function literal can be assigned to a variable, passed as an argument, or called immediately without ever being given a name.
greet := func(name string) string {
return fmt.Sprintf("Hello, %s!", name)
}
fmt.Println(greet("Gopher"))
shout := func(name string) string {
return fmt.Sprintf("HEY %s!", name)
}("world") // called immediately, right where it's defined
fmt.Println(shout)Hello, Gopher!
HEY world!A function can return more than one value. The standard library uses this constantly, pairing a result with an error.
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("divide by zero")
}
return a / b, nil
}
result, err := divide(10, 2)
fmt.Println(result, err)5 Naming return values declares them as variables at the top of the function, pre-set to their zero values. A bare return sends back their current values.
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
fmt.Println(split(17))7 10...T collects any number of trailing arguments into a []T inside the function. s... spreads an existing slice into that same call instead of passing one element at a time. any (an alias for interface{}) lifts the T-restriction entirely — fmt.Println itself is declared func Println(a ...any) (int, error).
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
fmt.Println(sum(1, 2, 3))
nums := []int{4, 5, 6}
fmt.Println(sum(nums...))
func logAll(args ...any) {
for _, a := range args {
fmt.Printf("%v ", a)
}
fmt.Println()
}
logAll("go", 1.26, true)6
15
go 1.26 true| Form | Description |
|---|---|
| func f(x, y int) int | Shared-type parameters: only the last one needs a type. |
| f := func(x int) int { ... } | Function literal, unnamed and assignable to a variable, since a function's type is a value like any other. |
| func f() (int, error) | Multiple return values, most often a result paired with an error. |
| func f() (n int, err error) | Named return values, pre-declared and zero-valued. A bare return sends back their current values. |
| func f(nums ...int) | Variadic parameter; nums is a []int inside the function body. |
| f(s...) | Spreads an existing slice into a variadic call. |
| func f(args ...any) | Variadic parameter of any type. This is fmt.Println's own signature. |
A naked return only reads well with a short function and few return values; past two or three, spelling out return x, y stays easier to follow than tracking a named variable's assignments.