ProgrammingGo Developer, Backend Developer

Explain the features of passing and returning large structures from functions in Go, and how it affects performance and program behavior.

Pass interviews with Hintsage AI assistant

Answer.

In Go, structures (struct) are passed and returned by value by default. This means that when a function is called or a value is returned, the entire structure is copied. For small structures, this is transparent, but for large ones, it becomes critical.

Background

Initially, Go was geared towards efficient operation with a small number of allocations. However, the danger of unintentional copying of large data arose when structures use many fields and nested objects. The performance of such operations can suffer, and sometimes the difference is only revealed in profiling or through GC pain.

The Problem

If a structure is large, copying it on every function call, return, or assignment becomes costly. This leads to:

  • Increased execution time;
  • Burden on the GC (copy-on-write for large fields, delayed memory cleanup);
  • Bugs, where changes made to the copy do not affect the original.

Solution

For large structures, it is advisable to pass and return a pointer to the structure (*T) instead of the object itself. This reduces overhead and allows working with a single instance of the data.

Example code:

package main import "fmt" type Large struct { Data [1024]int } // Passing by value (incorrect for large objects) func ValueProcess(l Large) { l.Data[0] = 123 // will only change the copy } // Passing by pointer func PointerProcess(l *Large) { l.Data[0] = 456 // changes the original } func main() { a := Large{} ValueProcess(a) fmt.Println("After ValueProcess:", a.Data[0]) // 0 PointerProcess(&a) fmt.Println("After PointerProcess:", a.Data[0]) // 456 }

Key points:

  • All structures are copied by value by default;
  • Passing the address (pointer) avoids copying;
  • Returning by value may be efficiently optimized by the compiler for small structures but not for large ones.

Tricky Questions.

1. Can you return a pointer to a local structure variable from a function in Go?

Yes. Go guarantees the validity of such pointers by automatically moving those values to the heap that the pointer returns (escape to heap).

func NewLarge() *Large { l := Large{} return &l }

2. Will the original change if a structure is passed to a function by value and the fields are modified inside?

No: only the copy will change, and the original outside the function will remain the same.

3. Should you always use pointers for structures?

No. For small (a few fields) structures, passing by value is safe and often preferable (immutable/value-semantic), saving on allocations and reducing the load on the GC.

Common Mistakes and Anti-patterns

  • Returning large structures and passing them to functions by value unnecessarily;
  • Unjustified use of pointers for trivial structs;
  • Data mutability errors: accidentally updating only the copy, not the original.

Real-life Example

Negative Case

In a logging service, each event represented a large structure and was returned from functions by value — every change copied the entire structure.

Pros:

  • The code was simple and safe for small structures.

Cons:

  • There was an increase in memory consumption, the GC triggered frequently, and the service started to lag.

Positive Case

They switched to passing and returning structures by pointer, modifying data through signatures like func(l *Large) and func() *Large.

Pros:

  • Minimal copying, less load on the GC, faster processing.

Cons:

  • It required controlling mutability, avoiding accidental side effects when working with a single object.