Marcell Ciszek Druzynski

Pass maps as arguments to a function

Maps in go are reference types and the values that it holds can be changed even when not passing in a pointer type

November 20, 2023

Intro

In this post, I'll discuss the behavior of maps in Go when passed as arguments into functions. I'll also discuss how to avoid modifying the original map when passing it to a function.

Maps in Go

Maps in Go are a collection of key-value pairs. They are used to store and retrieve values based on a unique key.

1score := map[string]int{
2 "Tony": 30,
3 "Steve": 10,
4 "Thor": 2,
5}
1score := map[string]int{
2 "Tony": 30,
3 "Steve": 10,
4 "Thor": 2,
5}

Here we have a map called score that stores the scores of three players. Where the key is the player's name and the value is the player's score.

Passing maps as arguments to a function

In Go, when we pass an argument to a function, it can be passed either by value or by reference. Let's illustrate this through an example:

1func main() {
2 var score int = 10
3 fmt.Println(score) // 10
4 incrementScore(score)
5 fmt.Println(score) // 10
6}
7
8func incrementScore(score int) {
9 score++
10}
1func main() {
2 var score int = 10
3 fmt.Println(score) // 10
4 incrementScore(score)
5 fmt.Println(score) // 10
6}
7
8func incrementScore(score int) {
9 score++
10}

In this example, we pass the score into the incrementScore() function, but the printed value remains 10. This is because arguments are passed by value in Go. To modify the original value, we can pass the score as a reference by changing the function to accept a pointer to an int variable:

1func main() {
2 var score int = 10
3 fmt.Println(score) // 10
4 incrementScore(&score)
5 fmt.Println(score) // 11
6}
7
8func incrementScore(score *int) {
9 *score++
10}
1func main() {
2 var score int = 10
3 fmt.Println(score) // 10
4 incrementScore(&score)
5 fmt.Println(score) // 11
6}
7
8func incrementScore(score *int) {
9 *score++
10}

Now, the score variable has been successfully mutated, and its value is 11. We use a pointer to modify the value of a variable from the incrementScore() function.

Now, let's shift our focus to maps in Go. Maps are reference types, meaning that when we pass a map to a function, we pass a reference to the underlying data structure, not a copy of the map. Although maps themselves are not mutable, the data they reference is mutable:

1func main() {
2 score := map[string]int{
3 "Tony": 30,
4 "Steve": 10,
5 "Thor": 2,
6 }
7 fmt.Println(score) // map[Steve:10 Thor:2 Tony:30]
8 changeScores(score)
9 fmt.Println(score) // map[Steve:10 Thor:2 Tony:1000]
10}
11
12func changeScores(scores map[string]int) {
13 scores["Tony"] = 1000
14}
1func main() {
2 score := map[string]int{
3 "Tony": 30,
4 "Steve": 10,
5 "Thor": 2,
6 }
7 fmt.Println(score) // map[Steve:10 Thor:2 Tony:30]
8 changeScores(score)
9 fmt.Println(score) // map[Steve:10 Thor:2 Tony:1000]
10}
11
12func changeScores(scores map[string]int) {
13 scores["Tony"] = 1000
14}

In this example, modifications made to the map inside the changeScores() function are reflected in the original map outside the function. To avoid modifying the original map, we can create a copy of the map inside the function:

1func main() {
2 score := map[string]int{
3 "Tony": 30,
4 "Steve": 10,
5 "Thor": 2,
6 }
7 fmt.Println(score)
8 changedScore := changeScores(score)
9 fmt.Println(changedScore)
10}
11
12func changeScores(scores map[string]int) map[string]int {
13 newScores := make(map[string]int)
14 for key, value := range scores {
15 newScores[key] = value
16 }
17 newScores["Tony"] = 1000
18 return newScores
19}
1func main() {
2 score := map[string]int{
3 "Tony": 30,
4 "Steve": 10,
5 "Thor": 2,
6 }
7 fmt.Println(score)
8 changedScore := changeScores(score)
9 fmt.Println(changedScore)
10}
11
12func changeScores(scores map[string]int) map[string]int {
13 newScores := make(map[string]int)
14 for key, value := range scores {
15 newScores[key] = value
16 }
17 newScores["Tony"] = 1000
18 return newScores
19}

To summarize:

  • Maps in Go are reference types.
  • Passing a map to a function passes a reference to the underlying data structure.
  • Modifications to the map inside a function affect the original map outside the function.
  • The map itself is immutable.
  • To avoid modifying the original map, create a copy of the map before making changes inside a function.