Skip to content
Theory · Functions · lesson 1 of 10

Parameters are copies

What you'll learn

What you'll learn:

  • explain why a function changes only a copy of its parameter, and return a new value with return;
  • return several results from a function and discard the ones you don't need with _;
  • write recursive functions with a base case;
  • tell where a variable is visible and what shadowing is.
A copier double takes a copy of the sheet x = 5 and puts out a sheet n = 10, while the sheet x = 5 stays on the glass

A function is handy to picture as a machine with a photocopier inside. You lay a sheet with a number on the glass, the machine does something with that number and hands out a finished sheet:

  • what you lay on the glass are the function's parameters;
  • the sheet that comes out is the result it returns with return.

This machine has one peculiarity: it never touches your sheet. Before it starts, it takes a copy and works on the copy, while the original stays on the glass.

Let's see how this looks in code. The function double doubles n, while the variable x in main stays the same:

GO
package main

import "fmt"

func double(n int) {
    n = n * 2
    fmt.Println("inside:", n) // inside: 10
}

func main() {
    x := 5
    double(x)
    fmt.Println("outside:", x) // outside: 5
}

Pass by value

Let's walk through the example line by line:

  1. x := 5 creates the variable x in main.
  2. When double(x) is called, the value of the argument x is copied into the parameter n.
  3. n = n * 2 turns the copy into 10.
  4. The first Println prints inside: 10.
  5. The function returns, and the second Println prints outside: 5.

This walkthrough used two words that people often mix up:

  • a parameter is a name in the function's declaration: n in func double(n int);
  • an argument is the value passed at the call: x in double(x).

This way of passing is called pass by value. The function gets a copy of the argument, not the variable itself.

The machine takes a copy on every run, and Go copies the argument on every call. For a number this is a fast operation, and you never notice it.

Interviewers like to ask this: "Does Go pass arguments by value or by reference?" The answer has two parts:

  • always by value, and this holds for every argument, not only numbers;
  • assigning to a parameter never changes the caller's variable.

A parameter is a separate variable

A parameter is an ordinary variable of the function:

  • you can read it, change it with = and ++, and pass it to other calls;
  • it is created on every call and disappears when the function returns;
  • it has no connection to the caller's variable.

There are two cases where a connection seems to exist, though there is none.

An expression as the argument

An argument can be an expression, not only a variable. In the call double(x + 1) the sum 6 is computed first, and its value goes into n. The function sees only the number and does not know how it was produced.

Matching names

Matching names connect nothing either. If you name the parameter x, like the variable in main, there are still two variables. Each is visible only in its own function, and changing one does not touch the other.

Return a new value

A function connects to the calling code in only two ways:

  • arguments going in;
  • a result coming out.

For the caller to get the changed value, the function returns it with return. The caller decides what to do with the result: print it, store it in a new variable, or write it back into x.

GO
package main

import "fmt"

func doubled(x int) int {
    x = x * 2
    return x
}

func main() {
    x := 5
    fmt.Println(x, doubled(x)) // 5 10
    x = doubled(x)
    fmt.Println(x) // 10
}

Let's walk through main line by line:

  1. x := 5 creates the variable x in main.
  2. fmt.Println(x, doubled(x)) calls doubled: the function changes its parameter x and returns 10, while x in main stays 5. The line prints 5 10.
  3. x = doubled(x) writes the result of the second call back into x.
  4. fmt.Println(x) prints 10: the variable changed only after the assignment.

The machine handed out a new sheet, and you put it in place of the old one yourself.

Common mistake

The function returns a new value, but the result of the call is not stored anywhere:

GO
x := 5
doubled(x) // ✗
fmt.Println(x)

The program compiles and prints 5. A function call can be written as a statement on its own even when it returns something, and then the result is lost. The dish is ready, but nobody took it. The compiler does not warn about this, so assign the result explicitly: x = doubled(x).

Key points

  • A parameter is a copy of the argument: changes inside the function are not visible outside.
  • Go always passes arguments by value.
  • For the caller to get a new value, the function returns it and the caller assigns it: x = doubled(x).

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way