Declaring a function
A function declaration is func, a name, the parameters, the result type, and a body. The type goes after the parameter name, not before it.
add takes two int values and returns an int. greet returns nothing, so its signature has no result type and its body needs no return.
A function that declares a result must end in a terminating statement. If some path can reach the closing brace without returning, the compiler stops with missing return.
Parameters that share a type
Consecutive parameters of the same type can share one type name. func add(a, b int) int means exactly the same as func add(a int, b int) int. Mixing is allowed too:
func scale(x, y float64, factor int) (float64, float64)
Here x and y are float64 and factor is an int.
Return values
A function can return zero, one, or several values. Several results go in parentheses, and the caller receives them with a multiple assignment:
This prints 3 2. The (value, error) pair is the most common use of this feature in Go, and it has its own page on multiple return values.
Arguments are copied
Go passes every argument by value. The function works on a copy, so assigning to a parameter never changes the caller's variable.
The output is:
{1 2}
{100 2}
[99 2 3]
The third line surprises people. A slice value is a small header (pointer, length, capacity). The copy points at the same backing array, so writing s[0] writes the caller's element. But append inside the function may allocate a new array, and the caller never sees that new header. If a function grows a slice, return the new slice. The same logic applies to maps: the function can add keys the caller sees, because the map value refers to shared data.
Use a pointer parameter when the function must modify the caller's variable, or when the value is a large struct you do not want to copy on every call.
Functions are values
A function has a type, written without the name: func(int, int) int. You can store a function in a variable, put it in a map or slice, and pass it to another function.
square is an anonymous function assigned to a variable. Anonymous functions can also read and change variables from the surrounding scope, which makes them closures.
A named function type makes signatures easier to read when the same shape appears in many places:
type Transform func(int) int
func apply(xs []int, f Transform) []int
The zero value of a function type is nil. Calling a nil function panics with invalid memory address or nil pointer dereference, so check optional callbacks before calling them. Function values can only be compared to nil, never to each other.
Recursion
A function can call itself. Go has no tail-call optimization, but goroutine stacks grow on demand, so ordinary recursion depths are fine.
for i := range 6 counts from 0 to 5 and needs Go 1.22 or later.
What Go functions do not have
- No overloading. Two functions in one package cannot share a name, even with different parameters.
- No default or named arguments. Every parameter is passed at every call, in order. A config struct gives you named, optional fields:
NewServer(Config{Port: 8080}). - No nested named functions. Inside a function body you can only declare anonymous functions, usually assigned to a variable.
Variable-length argument lists are supported through variadic functions.
Exported and unexported functions
A function whose name starts with an uppercase letter (Parse) is exported and callable from other packages. A lowercase name (parse) is visible only inside its own package. There are no public or private keywords; capitalization is the whole rule.
Common mistakes
- Expecting a parameter change to reach the caller. Assigning to a parameter only changes the copy. Return the new value or take a pointer.
- Appending inside a function and ignoring the result.
func add(s []int) { s = append(s, 1) }has no effect on the caller's slice length. Writefunc add(s []int) []intand uses = add(s). - Unused results. Go allows ignoring a return value.
go vetdoes not flag a dropped error, but linters such aserrcheck(part ofgolangci-lint) do. Use_ =only when ignoring is a decision.
Frequently Asked Questions
How do you define a function in Go?
Use the func keyword, a name, a parameter list with types after the names, and the result type: func add(a, b int) int { return a + b }. A function with no result omits the type, and a function with several results lists them in parentheses: func divmod(a, b int) (int, int).
Does Go pass arguments by value or by reference?
Always by value. The function gets a copy of each argument. For an int or a struct that copy is independent of the caller's variable. Slices, maps, channels and pointers are small values that refer to shared data, so changes to the elements they point at are visible to the caller, while reassigning the parameter itself is not. Pass a pointer (*T) when the function must change the caller's variable.
Can you pass a function as a parameter in Go?
Yes. Functions are values with a type such as func(int) int. Declare a parameter of that type and pass any function with a matching signature, named or anonymous: func apply(xs []int, f func(int) int).
Does Go support function overloading or default parameters?
No. Each function name in a package must be unique, and every parameter must be passed at every call. The usual substitutes are distinct names (NewServer, NewServerWithTLS), a config struct, variadic parameters, or functional options.