Practical examples of defining and using structs in Go
Simple examples of defining and using structs in Go
Before jumping into bigger patterns, let’s start with a small, realistic example of a struct in Go that models a user in an internal tool:
package main
import "fmt"
type User struct {
ID int
Email string
IsActive bool
}
func main() {
// Literal initialization with field names
u1 := User{
ID: 1,
Email: "alice@example.com",
IsActive: true,
}
// Positional initialization (works, but easier to break)
u2 := User{2, "bob@example.com", false}
fmt.Println(u1)
fmt.Println(u2)
}
This tiny snippet already shows a few examples of defining and using structs in Go:
- Declaring a struct type with three fields.
- Initializing it with named fields (safer, more readable).
- Initializing it positionally (shorter, but brittle if you reorder fields).
In real projects, you almost always prefer the named style because it survives refactors.
Real examples of struct methods and pointer vs value receivers
Once you define a struct, you usually attach behavior to it with methods. Here’s an example of a User struct with methods that show how methods work in Go:
type User struct {
ID int
Email string
IsActive bool
}
// Value receiver: does not modify original
func (u User) DisplayName() string {
if u.Email == "" {
return "Unknown user"
}
return u.Email
}
// Pointer receiver: can modify original
func (u *User) Deactivate() {
u.IsActive = false
}
And a short example of using these methods:
func main() {
u := User{ID: 42, Email: "dev@example.com", IsActive: true}
fmt.Println(u.DisplayName()) // "dev@example.com"
u.Deactivate() // pointer receiver, modifies u
fmt.Println(u.IsActive) // false
}
This is one of the best examples of how Go nudges you to think about mutability. Methods with pointer receivers are common when you’re updating state, while value receivers are fine for read-only helpers.
For deeper background on Go’s type system and methods, the official Go tour remains a solid reference:
https://go.dev/tour/methods/1
HTTP handler examples of defining and using structs in Go
In 2024, a lot of Go code lives behind HTTP. Here’s a realistic example of a struct used with Go’s net/http package to implement a handler with dependencies.
package main
import (
"encoding/json"
"log"
"net/http"
)
type Logger interface {
Println(v ...any)
}
type UserService interface {
FindByID(id int) (*User, error)
}
type User struct {
ID int `json:"id"`
Email string `json:"email"`
}
type UserHandler struct {
Logger Logger
UserService UserService
}
func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// For brevity, pretend we parsed ID=1 from the URL
user, err := h.UserService.FindByID(1)
if err != nil {
http.Error(w, "user not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(user); err != nil {
h.Logger.Println("encode error:", err)
}
}
This snippet shows one of the most common real examples of defining and using structs in Go:
Useris a data struct with JSON tags.UserHandleris a struct that groups dependencies (logger and service).UserHandlerimplementshttp.Handlervia a method on a pointer receiver.
Instead of wiring everything with global variables, you organize state with structs, which keeps your code testable and easier to reason about.
JSON API examples include tags, omitempty, and custom field names
Go’s encoding/json package is still the default choice for JSON in 2024. Here’s an example of a struct that models a typical API response:
type APIError struct {
Code string `json:"code"`
Message string `json:"message"`
}
type UserResponse struct {
ID int `json:"id"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
// Optional fields
Phone *string `json:"phone,omitempty"`
Address *UserAddress `json:"address,omitempty"`
// Nested struct
Errors []APIError `json:"errors,omitempty"`
}
type UserAddress struct {
Line1 string `json:"line1"`
City string `json:"city"`
State string `json:"state"`
ZipCode string `json:"zip_code"`
}
This gives you several concrete examples of defining and using structs in Go for JSON:
- Using
omitemptyso optional fields don’t clutter responses. - Pointers for optional nested data (
Phone,Address). - Nested structs for structured errors.
When you encode a UserResponse, Go respects the JSON tags and omits empty optional fields, which keeps your API clean and friendly to front-end teams.
For a reference on JSON tags and behavior, the Go encoding/json docs are worth bookmarking:
https://pkg.go.dev/encoding/json
Configuration struct example of organizing app settings
Most Go services load configuration from environment variables, flags, or files. A config struct is a simple pattern that shows up in almost every production codebase.
package config
import "time"
type HTTPConfig struct {
Addr string // ":8080"
ReadTimeout time.Duration // 5 * time.Second
WriteTimeout time.Duration // 10 * time.Second
}
type DBConfig struct {
DSN string
MaxOpenConns int
MaxIdleConns int
ConnMaxIdle time.Duration
ConnMaxLifetime time.Duration
}
type AppConfig struct {
Env string // "dev", "staging", "prod"
HTTP HTTPConfig
DB DBConfig
}
Then in main.go you might see:
func loadConfig() AppConfig {
return AppConfig{
Env: "dev",
HTTP: HTTPConfig{
Addr: ":8080",
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
},
DB: DBConfig{
DSN: "postgres://...",
MaxOpenConns: 10,
MaxIdleConns: 5,
ConnMaxIdle: 5 * time.Minute,
ConnMaxLifetime: 1 * time.Hour,
},
}
}
This is a clean example of defining and using structs in Go to group related settings:
AppConfigembedsHTTPConfigandDBConfigas fields.- Each sub-struct keeps logically related configuration together.
As Go projects grow, this pattern scales well and stays readable.
Embedding and composition: best examples for code reuse
Go doesn’t have inheritance, but it does have embedding. Here’s a practical example of using struct embedding to share common fields across multiple database models.
package model
import "time"
type BaseModel struct {
ID int `db:"id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
type User struct {
BaseModel
Email string `db:"email"`
IsActive bool `db:"is_active"`
}
type Order struct {
BaseModel
UserID int `db:"user_id"`
Total float64 `db:"total"`
}
By embedding BaseModel, both User and Order get ID, CreatedAt, and UpdatedAt directly:
func touch(m *BaseModel) {
m.UpdatedAt = time.Now()
}
func exampleUsage() {
u := User{Email: "alice@example.com"}
touch(&u.BaseModel)
}
This is one of the best examples of defining and using structs in Go for code reuse that still keeps things explicit. You’re not hiding behavior behind an inheritance tree; you’re composing structs from smaller building blocks.
The official Go blog has a good discussion on embedding and composition patterns:
https://go.dev/blog/laws-of-reflection
Concurrency-safe examples include structs with mutexes
Go’s concurrency story is still a big reason teams adopt it in 2024. When you share state across goroutines, you often wrap it in a struct with a mutex.
package counter
import "sync"
type Counter struct {
mu sync.Mutex
n int
}
func (c *Counter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.n++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.n
}
Usage example:
func main() {
var wg sync.WaitGroup
c := &Counter{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
c.Inc()
}()
}
wg.Wait()
fmt.Println(c.Value()) // 100
}
This pattern shows a real example of defining and using structs in Go to safely share mutable state across goroutines. The mutex is just another field, and the struct methods enforce correct locking.
Generics-era examples of typed collections in Go
With Go 1.22 widely adopted in 2024–2025, generics are no longer experimental. You’ll see more examples of defining and using structs in Go that pair nicely with generic helpers.
Here’s a simple typed wrapper around a slice that tracks pagination metadata:
type Page[T any] struct {
Items []T
Page int
PageSize int
TotalItems int
}
func (p Page[T]) TotalPages() int {
if p.PageSize == 0 {
return 0
}
n := p.TotalItems / p.PageSize
if p.TotalItems%p.PageSize != 0 {
n++
}
return n
}
Usage with a concrete type:
type Product struct {
ID int
Name string
Price float64
}
func examplePage() {
products := []Product{
{ID: 1, Name: "Keyboard", Price: 49.99},
{ID: 2, Name: "Mouse", Price: 29.99},
}
p := Page[Product]{
Items: products,
Page: 1,
PageSize: 20,
TotalItems: 2,
}
fmt.Println("total pages:", p.TotalPages())
}
This is a modern example of defining and using structs in Go side by side with generics to make reusable, type-safe helpers.
Testing and mocking: example of using structs for fake services
Testing is where structs quietly shine. You can create small fake implementations for interfaces by defining lightweight structs.
type EmailSender interface {
Send(to, subject, body string) error
}
type FakeEmailSender struct {
Sent []struct {
To string
Subject string
Body string
}
}
func (f *FakeEmailSender) Send(to, subject, body string) error {
f.Sent = append(f.Sent, struct {
To string
Subject string
Body string
}{to, subject, body})
return nil
}
Then in a test:
func TestWelcomeEmail(t *testing.T) {
fake := &FakeEmailSender{}
err := SendWelcomeEmail(fake, "user@example.com")
if err != nil {
t.Fatal(err)
}
if len(fake.Sent) != 1 {
t.Fatalf("expected 1 email, got %d", len(fake.Sent))
}
}
This is a practical example of defining and using structs in Go to keep tests fast and independent of external systems.
For general software testing patterns and reliability concepts, even health-focused sites like the U.S. National Library of Medicine at the National Institutes of Health discuss the value of rigorous testing and validation in their research workflows:
https://www.nlm.nih.gov
The domain is different, but the mindset—test, measure, iterate—translates well to software.
FAQ: short examples-focused answers
What are some simple examples of defining and using structs in Go?
A very simple example is a User struct with a couple of fields and a helper method:
type User struct {
ID int
Email string
}
func (u User) DisplayName() string {
if u.Email == "" {
return "Unknown"
}
return u.Email
}
You can then initialize and use it:
u := User{ID: 1, Email: "dev@example.com"}
fmt.Println(u.DisplayName())
Can you give an example of nested structs in Go?
Yes. A common pattern is nesting an address inside a user profile:
type Address struct {
City string
State string
}
type Profile struct {
Name string
Address Address
}
p := Profile{
Name: "Alice",
Address: Address{City: "Boston", State: "MA"},
}
This is an everyday example of defining and using structs in Go to represent structured data.
How do I choose between pointer and value receivers on struct methods?
If a method needs to modify the struct, or the struct is large enough that copying it would be wasteful, use a pointer receiver:
func (c *Counter) Inc() { c.n++ }
If the method is read-only and the struct is small, a value receiver is fine:
func (u User) DisplayName() string { return u.Email }
Being consistent within a type is more important than micro-optimizing every method.
Are there best examples of struct usage patterns I should learn first?
Yes, a few patterns show up constantly:
- Data models with JSON tags for APIs.
- Config structs that group settings.
- Handler structs that bundle dependencies.
- Embedding base structs for shared fields.
If you can read and write these examples of defining and using structs in Go comfortably, you’re in good shape for most day-to-day Go work.
Related Topics
Practical examples of Go command-line application examples for 2025
Practical examples of 3 examples of working with Go maps for real projects
Practical examples of simple HTTP server examples in Go
Practical examples of defining and using structs in Go
Practical examples of creating and using Go interfaces
So You Think You Know HTTP in Go? Think Again
Explore More Go Code Snippets
Discover more examples and insights in this category.
View All Go Code Snippets