Understanding Private Fields in Go
James Reed
Infrastructure Engineer · Leapcell

Key Takeaways
- Private fields in Go are defined by starting with a lowercase letter and are only accessible within the same package.
- Reflection and the
unsafe
package can be used to access private fields but should be avoided due to risks. - Best practices recommend using getter/setter methods or reconsidering design instead of bypassing encapsulation.
In Go, the visibility of struct fields is determined by the capitalization of their names. Fields whose names start with an uppercase letter are exported and accessible from other packages, while fields starting with a lowercase letter are unexported (private) and accessible only within the same package. This design encourages encapsulation and controlled access to data.
Defining Private Fields
To define a struct with private fields, start the field names with lowercase letters:
package user type Info struct { name string age int } func NewUser(name string, age int) Info { return Info{ name: name, age: age, } }
In this example, the Info
struct has two private fields, name
and age
, which are unexported and cannot be accessed directly from other packages.
Accessing Private Fields Within the Same Package
Within the same package, you can access and modify private fields directly:
package user func (i *Info) UpdateName(newName string) { i.name = newName } func (i *Info) GetName() string { return i.name }
These methods allow controlled access to the private name
field within the user
package.
Accessing Private Fields from Other Packages
Accessing private fields from outside their defining package is generally discouraged and not directly possible. However, Go provides advanced techniques like using the unsafe
package or reflection to bypass these restrictions. These methods should be used with caution, as they can lead to fragile code and break encapsulation principles.
Using the unsafe
Package
The unsafe
package allows for low-level memory manipulation, enabling access to private fields by calculating their memory offsets:
package main import ( "fmt" "unsafe" "user" ) func main() { u := user.NewUser("Alice", 30) pName := (*string)(unsafe.Pointer(&u)) fmt.Println(*pName) // Outputs: Alice pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&u)) + unsafe.Sizeof(string("")))) fmt.Println(*pAge) // Outputs: 30 }
In this example, unsafe.Pointer
is used to convert the address of u
to a generic pointer, and uintptr
arithmetic calculates the offsets to access the private fields. This approach is risky and should be avoided in production code.
Using Reflection
Reflection provides another way to access private fields, though it also has limitations and risks:
package main import ( "fmt" "reflect" "user" ) func main() { u := user.NewUser("Alice", 30) v := reflect.ValueOf(&u).Elem() nameField := v.FieldByName("name") if nameField.CanSet() { nameField.SetString("Bob") } else { fmt.Println("Cannot set private field 'name'") } }
In this code, reflection is used to obtain the name
field. However, attempting to set a private field using reflection will result in a runtime panic, as Go's reflection package enforces access controls. Therefore, while you can read private fields using reflection, modifying them is not allowed.
Best Practices
While it's technically possible to access private fields using unsafe
or reflection, it's generally best to respect the encapsulation intended by the package author. Instead of bypassing access controls, consider the following approaches:
-
Use Getter and Setter Methods: If the package provides public methods to access or modify private fields, use them.
-
Fork or Contribute: If you need access to private fields that aren't exposed, consider forking the package or contributing to it by adding the necessary accessors.
-
Reevaluate Design: Sometimes, needing access to private fields indicates a design issue. Reevaluate your code's design to see if there's a better approach that doesn't require breaking encapsulation.
Respecting the privacy of struct fields ensures that packages maintain control over their internal state, leading to more maintainable and robust codebases.
FAQs
You can't directly, but unsafe
and reflection can be used (not recommended).
Go simplifies visibility rules by relying on naming conventions instead of explicit keywords.
Use public getter/setter methods if provided by the package.
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