Content posted here with the permission of the author Anuj Verma, who is currently employed at Josh Software. Original post available here.
As a Go programmer or while learning Go you might have heard that everything in Go is passed by value.
Also in the official FAQ it says that:
As in all languages in the C family, everything in Go is passed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter. For instance, passing an int value to a function makes a copy of the int, and passing a pointer value makes a copy of the pointer, but not the data it points to.
What does that mean ?
It means that if you pass a variable to a function, the function always gets a copy of it. Remember always. So the caller and callee have two independent variables with the same value. Hence, If the callee modifies the parameter variable, the effect is not visible to the caller.
Lets prove it via an example
package main import "fmt" type person struct { Name string } func main() { p := person{Name: "Smith"} fmt.Println("Value of name before calling updateName() is: ", p.Name) updateName(p) fmt.Println("Value of name after calling updateName() is: ", p.Name) } func updateName(p person) { p.Name = "John" }
Output:
Value of name before calling updateName() is: Smith Value of name after calling updateName() is: Smith
As you can clearly see that the value of name even after calling updateName function is unchanged.
Lets try the same with Go slices
I tried the same on Go slices and it demonstrated some pretty interesting or you can say surprising behaviour. Lets quickly have a look at the code below:
package main import ( "fmt" ) func main() { greetings := []string{"Hi", "Welcome", "Hola"} updateGreetings(greetings) fmt.Println(greetings) } func updateGreetings(greetings []string) { greetings[0] = "नमस्ते" }
Output:
[नमस्ते Welcome Hola]
As you can see the value of first element of slice greeting is changed. Now this is completely opposite of what we have seen for Go struct.
Why slices are behaving differently ?
We are seeing this change in behaviour because the way Go slices are implemented internally. Lets take a minute to understand how Go slices are implemented. Have a look at the diagram below:
So when we make a slice of string, Go internally is creating two separate data structures.
The first is what we refer to as the slice. The slice is a data structure that has 3 elements inside it:
- Pointer to array: is a pointer over to the underlying array that represents the actual list of items.
- Capacity: is how many elements it can contain at present
- Length: is the number of elements referred to by the slice
The second is the actual array that represents the actual list of items. So lets have a look at what happens in memory when we declare a slice.
As you can see the slice at address 0002 is pointing to array stored at address 0003. Lets have a look at what happens when we pass the greetings slice to function updateGreetings.
As you can see, Go is still behaving as a pass by value language, as it is making of a copy of the slice data structure at address 0005. Now here is the very important thing, even though the slice data structure is copied, it is still pointing at the original array in memory at address 0003.
When we modify the slice inside the function, we are modifying the same array that both copies of slice pointing to. So in Go slices are what referred as reference types.
Are there any more reference types in Go ?
So slices are not the only data structure that behave in this fashion, there are also other types which behave exactly the same way. In the below diagram, I have segregated the value types and reference types in Go.
The point to note here is that while passing on reference types, we do not need to pass address of the type. Go will handle it and any change in the variable will be reflected in the caller function. When we are passing value types like int, bool etcand we expect that the changes in value should be reflected, we must use pointers.
Reference:
- https://www.udemy.com/go-the-complete-developers-guide/
- https://blog.golang.org/go-slices-usage-and-internals
Conclusion
This is one Go gotcha which can lead to many many issues when we start Go programming. Just keep in mind the diagram of value and reference types. Hope this post helps you to avoid getting into issues in your program. Thanks for reading. Please like and share the post so that it can reach to other valuable readers too.
The course that you’ve referred is not a good course. He’s just a web developer who talks about Go. There are no reference types in Go. Also, if the function changes the slice, the caller won’t see the change.