How to Copy a Struct in Golang
James Reed
Infrastructure Engineer · Leapcell

Key Takeaways
- Shallow copy uses direct assignment but does not work well for reference types.
- Deep copy requires manual allocation for pointers, slices, and maps to avoid unintended modifications.
- JSON serialization provides an easy deep copy solution but has performance overhead.
Copying structs in Golang is a common requirement, whether for cloning data, avoiding unintended modifications, or handling concurrent operations. This article explains various ways to copy structs in Go, including shallow copy, deep copy, and best practices.
Shallow Copy of a Struct
A shallow copy in Go means creating a new struct with the same values as the original. This works well if the struct contains only primitive types or values that don't involve pointers.
Using Assignment
The simplest way to copy a struct is through direct assignment:
package main import "fmt" type Person struct { Name string Age int } func main() { original := Person{Name: "Alice", Age: 30} copy := original // Shallow copy copy.Name = "Bob" // Modifying the copy does not affect the original fmt.Println("Original:", original) // Output: {Alice 30} fmt.Println("Copy:", copy) // Output: {Bob 30} }
Since Go treats struct variables as value types, direct assignment creates a new copy of the struct in memory.
Deep Copy of a Struct
A deep copy ensures that all fields, including pointers and slices, are independently copied rather than sharing memory references.
Copying Struct with Pointers
If a struct contains pointer fields, direct assignment will copy the pointer reference rather than the actual data. This can lead to unintended side effects.
package main import "fmt" type Person struct { Name *string Age int } func deepCopy(p Person) Person { nameCopy := *p.Name // Dereferencing to create a new string value return Person{ Name: &nameCopy, Age: p.Age, } } func main() { name := "Alice" original := Person{Name: &name, Age: 30} copy := deepCopy(original) *copy.Name = "Bob" // Modifying the copy's Name does not affect the original fmt.Println("Original:", *original.Name) // Output: Alice fmt.Println("Copy:", *copy.Name) // Output: Bob }
Here, a new string variable (nameCopy
) is created to prevent modifying the original struct.
Copying Struct with Slices and Maps
Slices and maps are reference types, meaning copying them via assignment will not create a deep copy. Instead, you should manually allocate a new slice or map and copy each element.
package main import "fmt" type Person struct { Name string Scores []int } func deepCopy(p Person) Person { scoresCopy := make([]int, len(p.Scores)) copy(scoresCopy, p.Scores) // Using built-in copy function return Person{ Name: p.Name, Scores: scoresCopy, } } func main() { original := Person{Name: "Alice", Scores: []int{90, 85, 88}} copy := deepCopy(original) copy.Scores[0] = 100 // Changing copy does not affect the original fmt.Println("Original Scores:", original.Scores) // Output: [90 85 88] fmt.Println("Copy Scores:", copy.Scores) // Output: [100 85 88] }
The copy()
function is used to copy slice elements into a new slice, ensuring independence from the original.
Copying Structs Using JSON Encoding
For complex structs, JSON serialization is a convenient way to achieve deep copying.
package main import ( "encoding/json" "fmt" ) type Person struct { Name string Age int Tags []string } func deepCopy(original Person) Person { var copy Person data, _ := json.Marshal(original) // Serialize to JSON json.Unmarshal(data, ©) // Deserialize to a new struct return copy } func main() { original := Person{Name: "Alice", Age: 30, Tags: []string{"engineer", "gamer"}} copy := deepCopy(original) copy.Tags[0] = "artist" // Modifying copy does not affect original fmt.Println("Original Tags:", original.Tags) // Output: [engineer gamer] fmt.Println("Copy Tags:", copy.Tags) // Output: [artist gamer] }
While JSON-based copying is easy, it has some drawbacks:
- Performance overhead: Serialization and deserialization can be slower than manual copying.
- Type safety: JSON does not preserve Go-specific types like
interface{}
or function pointers.
Best Practices for Copying Structs
- Use direct assignment for simple structs: If a struct only contains value types (like
int
,float64
, orstring
), direct assignment is enough. - Manually copy pointers, slices, and maps: Ensure new allocations to prevent unintended modifications.
- Consider
json.Marshal
for deep copies: Useful for complex structs but may introduce performance costs. - Use a deep copy library for large projects: Libraries like
copier
(https://github.com/jinzhu/copier) provide generic solutions for deep copying.
Conclusion
Copying structs in Go depends on whether you need a shallow or deep copy. Simple assignments work for value-based structs, while deep copying requires manual allocation for pointer-based and reference types. Understanding these techniques helps you avoid common pitfalls and ensures data integrity in Go applications.
FAQs
Slices and maps are reference types, so assignment copies the reference, not the data.
Manually copying fields is the fastest; JSON serialization is easier but slower.
Use deep copy when the struct contains pointers, slices, or maps to prevent shared modifications.
We are Leapcell, your top choice for hosting Go projects.
Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:
Multi-Language Support
- Develop with Node.js, Python, Go, or Rust.
Deploy unlimited projects for free
- pay only for usage — no requests, no charges.
Unbeatable Cost Efficiency
- Pay-as-you-go with no idle charges.
- Example: $25 supports 6.94M requests at a 60ms average response time.
Streamlined Developer Experience
- Intuitive UI for effortless setup.
- Fully automated CI/CD pipelines and GitOps integration.
- Real-time metrics and logging for actionable insights.
Effortless Scalability and High Performance
- Auto-scaling to handle high concurrency with ease.
- Zero operational overhead — just focus on building.
Explore more in the Documentation!
Follow us on X: @LeapcellHQ