Passing Variables by Value vs. Passing by Pointer in Go Introduction
Constantine Yachnytskyi
Distributed Systems Engineer | Golang Software Developer | Rust Software Developer | Golang Software Engineer | Rust Software Engineer
Understanding the difference between passing variables by value or by pointer in Go can have a significant impact on both the performance and behavior of your programs. This decision can affect memory usage, speed, and scalability?—?factors that are crucial in performance-critical applications such as web servers or large-scale microservices. In this article, we’ll dive deep into these two approaches using code examples, benchmark results, and insights from the golang-examples repository to help you decide which method is best for your use case.
Pass by Value in?Go
Passing by value means providing a function with a copy of the variable. Modifications within the function do not affect the original variable.
Example: Passing a Struct by Value
import (
"fmt"
)
type User struct {
Name string
Age int
}
func updateUserByValue(u User) {
u.Name = "Updated Name"
fmt.Println("Inside updateUserByValue:", u)
}
func updateUserByValueAndReturn(u User) User {
u.Name = "Updated Name"
fmt.Println("Inside updateUserByValue:", u)
return u
}
func main() {
user := User{Name: "Alice", Age: 25}
fmt.Println("Before updateUserByValue:", user)
updateUserByValue(user)
fmt.Println("After updateUserByValue:", user)
fmt.Println("\nBefore updateUserByValueAndReturn:", user)
userUpdated := updateUserByValueAndReturn(user) // Passing by value (copy)
fmt.Println("After updateUserByValue:", userUpdated) // We see the change before pass by pointer.
}
Output:
Before updateUserByValue: {Alice 25}
Inside updateUserByValue: {Updated Name 25}
After updateUserByValue: {Alice 25}
Before updateUserByValueAndReturn: {Alice 25}
Inside updateUserByValue: {Updated Name 25}
After updateUserByValue: {Updated Name 25}
In this scenario, the original user struct remains unchanged after the updateUserByValue function call because only a copy of it was passed to the function. However, when using updateUserByValueAndReturn, the returned struct reflects the changes made within the function, demonstrating that the original struct remains unaffected unless explicitly updated through a return value.
Pass by Pointer in?Go
Passing by pointer means providing a function with the memory address of the variable. Modifications within the function will directly affect the original variable since the function works with the actual memory location, not a copy of the variable.
Example: Passing a Struct by?Pointer
import (
"fmt"
)
type User struct {
Name string
Age int
}
func updateUserByPointer(u *User) {
u.Name = "Updated Name"
fmt.Println("Inside updateUserByPointer:", *u)
}
func main() {
user := User{Name: "Alice", Age: 25}
fmt.Println("Before updateUserByPointer:", user)
updateUserByPointer(&user)
fmt.Println("After updateUserByPointer:", user)
}
Output:
Before updateUserByPointer: {Alice 25}
Inside updateUserByPointer: {Updated Name 25}
After updateUserByPointer: {Updated Name 25}
Here, the user variable is modified directly because a pointer (&user) is passed, allowing the function to alter the original data.
领英推荐
Benchmarking Pass by Value vs. Pass by?Pointer
To understand the performance implications, benchmarks were conducted using different struct sizes. The results are as follows:
Struct Definitions
type SmallStruct struct {
ID int
Name string
Price float64
Active bool
Score int
}
type MediumStruct struct {
ID int
Name string
Price float64
Active bool
Tags []string
Details map[string]string
Reviews []float64
Age int
Email string
Phone string
Address string
Rank int
Score int
}
type LargeStruct struct {
ID int
Name string
Price float64
Active bool
Tags []string
Details map[string]string
Reviews []float64
Address string
Phone string
Email string
Age int
Rank int
Score int
Friends []string
Hobbies []string
History []string
Settings map[string]interface{}
Data []int
IsValid bool
Location string
Department string
Position string
Skills []string
Projects []string
Languages []string
Birthdate string
Gender string
Nationality string
MaritalStatus string
Notes string
ExperienceYears int
Education string
Certifications []string
LastLogin string
IPAddress string
LastUpdated string
SocialMedia []string
Contacted bool
EmergencyContact string
Permissions []string
Bio string
AvatarURL string
TagsMap map[string]bool
}
Performance Analysis
Key Takeaways:
Best Practices: When to Use Pass by Value vs. Pass by?Pointer
Use pass by value:
Use pass by pointer:
Conclusion
The choice between passing by value and passing by pointer in Go has clear performance implications. While passing by value offers immutability and can be useful in certain scenarios, passing by pointer enhances performance by avoiding unnecessary copies. Moreover, when returning a modified value, passing by value can be helpful as it provides an explicit copy of the struct, leaving the original data unchanged. For performance-critical applications, particularly those handling larger data structures, passing by pointer is often the preferable choice.
For more in-depth analysis and the full source code, visit the golang-examples repository. Also, check out my YouTube video for additional insights and tips.