Question

Initialize Nested Structs in Go

Question

How can I initialize the nested Proxy struct inside a Go Configuration struct using a struct literal?

package main

type Configuration struct {
    Val string
    Proxy struct {
        Address string
        Port    string
    }
}

func main() {
    c := &Configuration{
        Val: "test",
        Proxy: {
            Address: "addr",
            Port:    "80",
        },
    }

    _ = c
}

The Proxy field is an anonymous nested struct. What is the correct syntax for providing its Address and Port values?

Short Answer

You will learn how Go struct literals initialize nested fields, why anonymous nested structs need their full type in a literal, and when defining a separate named struct type makes code easier to maintain.

Concept

A struct groups related values into one type. A struct can contain another struct as one of its fields, which is often called a nested struct.

In this example, Configuration has a Proxy field. Proxy is not a string or number: its value must be a struct with Address and Port fields.

type Configuration struct {
    Val string
    Proxy struct {
        Address string
        Port    string
    }
}

The inner type is anonymous because it has no name such as ProxyConfig. Go still knows its exact structure, but when you create a value for it in a struct literal, you must state that structure explicitly.

This matters because Go requires every composite literal to identify the type being created. Writing only { ... } is not valid Go syntax: braces alone do not tell the compiler which type should be constructed.

For small, one-off groupings, an anonymous nested struct can be acceptable. For reusable or meaningful concepts such as proxy, database, server, or user settings, a named type is usually clearer.

Mental Model

Think of Configuration as a labelled box containing two compartments:

  • Val holds one text value.
  • Proxy holds a smaller box with its own labels: Address and Port.

When filling the smaller box, Go asks you to identify what kind of box it is. With an anonymous struct, you describe the box shape again:

struct {
    Address string
    Port    string
}{
    Address: "addr",
    Port:    "80",
}

If you give that smaller box a name, such as ProxyConfig, you can use the name instead of repeating its shape.

Take Quiz

Syntax and Examples

A struct literal uses the form:

TypeName{
    FieldName: value,
}

For an anonymous nested struct, specify the nested struct type before its literal:

package main

import "fmt"

type Configuration struct {
    Val string
    Proxy struct {
        Address string
        Port    string
    }
}

func main() {
    c := Configuration{
        Val: "test",
        Proxy: struct {
            Address string
            Port    string
        }{
            Address: "addr",
            Port:    "80",
        },
    }

    fmt.Println(c.Proxy.Address) // addr
}

The type written after Proxy: must match the field's type exactly. The field names, their types, and their order must be the same.

A more maintainable approach is to name the inner type:

package main

import "fmt"

type ProxyConfig struct {
    Address string
    Port    string
}

type Configuration struct {
    Val   string
    Proxy ProxyConfig
}

func main() {
    c := Configuration{
        Val: "test",
        Proxy: ProxyConfig{
            Address: "addr",
            Port:    "80",
        },
    }

    fmt.Println(c.Proxy.Port) // 80
}

Using ProxyConfig{...} is shorter, easier to read, and reusable in other parts of the program.

Step by Step Execution

Consider this named-type version:

type ProxyConfig struct {
    Address string
    Port    string
}

type Configuration struct {
    Val   string
    Proxy ProxyConfig
}

func main() {
    c := Configuration{
        Val: "test",
        Proxy: ProxyConfig{
            Address: "127.0.0.1",
            Port:    "8080",
        },
    }

    address := c.Proxy.Address
    println(address)
}

Execution proceeds as follows:

  1. ProxyConfig defines a type with Address and Port string fields.
  2. Configuration defines a Val string and a Proxy field whose type is ProxyConfig.
  3. Configuration{...} creates one Configuration value.
  4. Val: "test" assigns the outer Val field.
  5. Proxy: ProxyConfig{...} creates a separate ProxyConfig value and assigns it to the outer Proxy field.
  6. c.Proxy.Address first accesses the nested Proxy value, then reads its Address field.
  7. The program prints 127.0.0.1.

If you write c := &Configuration{...}, Go creates a Configuration value and returns a pointer to it. Field access remains the same: c.Proxy.Address. Go automatically dereferences the pointer for this access.

Real World Use Cases

Nested structs are useful when values naturally belong together.

  • Application configuration: Group HTTP server settings, database credentials, logging options, and proxy settings.
  • API responses: Model nested JSON such as an order that contains customer and shipping-address objects.
  • HTTP clients: Keep proxy address, port, credentials, and timeout settings together.
  • Domain models: Represent an employee with nested contact information or an invoice with nested billing details.
  • Deployment settings: Organize service configuration into sections such as Database, Cache, and TLS.

For example, a web service may use:

type ServerConfig struct {
    Host string
    Port int
}

type AppConfig struct {
    Environment string
    Server      ServerConfig
}

This makes config.Server.Port self-explanatory and keeps related settings together.

Take Quiz

Real Codebase Usage

In production Go code, developers usually prefer named nested types when the inner data has a real purpose or is used in more than one place.

type DatabaseConfig struct {
    Host     string
    Port     int
    Name     string
    User     string
    Password string
}

type AppConfig struct {
    Debug    bool
    Database DatabaseConfig
}

Common patterns include:

  • Configuration loading: Decode JSON, YAML, or environment-derived values into nested configuration structs.
  • Validation: Validate nested settings before starting an application.
  • Defaults: Build a complete configuration value, then overwrite selected nested fields.
  • Dependency injection: Pass a small, focused config type such as DatabaseConfig to code that only needs database settings.
  • API models: Use nested types to match the structure of request and response data.

A validation function can use guard clauses for invalid nested values:

func (c Configuration) Validate() error {
    if c.Proxy.Address == "" {
        return fmt.Errorf("proxy address is required")
    }
    if c.Proxy.Port == "" {
        return fmt.Errorf("proxy port is required")
    }
    return nil
}

For configuration types shared across packages or reused in tests, a named type is generally preferable to repeating an anonymous struct definition.

Common Mistakes

Omitting the nested type

This does not compile:

c := Configuration{
    Proxy: {
        Address: "addr",
        Port:    "80",
    },
}

Go does not support an untyped {...} literal. For an anonymous nested struct, include the complete type:

Proxy: struct {
    Address string
    Port    string
}{
    Address: "addr",
    Port:    "80",
},

Using a type that does not match exactly

This also fails because Port has a different type:

Proxy: struct {
    Address string
    Port    int
}{
    Address: "addr",
    Port:    80,
},

The anonymous type must exactly match the field declaration. Alternatively, define a named type once and reuse it.

Forgetting commas in multiline literals

Go requires a trailing comma after each element in a multiline struct literal:

c := Configuration{
    Val: "test", // comma required
}

Confusing a value field with a pointer field

If the field is Proxy ProxyConfig, assign a value with ProxyConfig{...}. If it is Proxy *ProxyConfig, assign a pointer with &ProxyConfig{...}.

type Configuration struct {
    Proxy *ProxyConfig
}

c := Configuration{
    Proxy: &ProxyConfig{Address: "addr", Port: "80"},
}

Overusing anonymous structs

Anonymous structs are valid, but repeating their definition makes code harder to change. Name the type when it represents a meaningful concept, such as ProxyConfig.

Comparisons

ApproachExampleBest forTrade-off
Anonymous nested structProxy struct { Address string; Port string }Small, local, one-off data shapesLiteral must repeat the full type
Named nested structProxy ProxyConfigReusable, meaningful configuration or domain dataRequires one additional type declaration
Nested struct valueProxy ProxyConfigA proxy is always present; zero values are acceptableCannot represent absence without another flag or convention
Nested struct pointerProxy *ProxyConfigProxy settings are optional or may be absentMust handle nil safely

A named type is often the clearest choice:

type ProxyConfig struct {
    Address string
    Port    string
}

Use an anonymous type mainly when the structure is tightly tied to one containing type and is unlikely to be reused.

Take Quiz

Cheat Sheet

// Anonymous nested struct field
type Configuration struct {
    Proxy struct {
        Address string
        Port    string
    }
}

// Initialize an anonymous nested struct
c := Configuration{
    Proxy: struct {
        Address string
        Port    string
    }{
        Address: "addr",
        Port:    "80",
    },
}
// Preferred for reusable concepts
type ProxyConfig struct {
    Address string
    Port    string
}

type Configuration struct {
    Proxy ProxyConfig
}

c := Configuration{
    Proxy: ProxyConfig{
        Address: "addr",
        Port:    "80",
    },
}
  • Use Field: value for keyed struct literals.
  • Multiline literals need trailing commas.
  • Anonymous struct types must match exactly.
  • Use &Configuration{...} when you need a pointer to the outer struct.
  • Use *ProxyConfig and &ProxyConfig{...} only when the nested field should be optional or shared.

FAQ

Why does Proxy: { ... } fail in Go?

A composite literal needs a type. { ... } alone has no type, so Go cannot determine what value to create.

Can Go infer the anonymous nested struct type from the field?

No. In a struct literal, Go requires the explicit anonymous struct type for that nested literal.

Should I use an anonymous struct or a named struct type?

Use a named type when the nested data represents a concept, might be reused, needs methods, or should be easier to read. Use an anonymous struct for small, local shapes.

How do I access a field in a nested struct?

Use dot notation for each level:

address := c.Proxy.Address

How do I initialize an optional nested struct in Go?

Use a pointer field and assign a pointer:

Proxy: &ProxyConfig{Address: "addr", Port: "80"}

Check that the field is not nil before reading it.

Can a nested struct have zero values?

Yes. If you do not initialize string fields, they are "". Integer fields become 0, booleans become false, and nested value structs contain the zero values of their fields.

Can I use positional fields instead of field names?

Yes, but keyed fields are safer and clearer:

ProxyConfig{"addr", "80"}

This depends on declaration order, so ProxyConfig{Address: "addr", Port: "80"} is usually preferred.

Take Quiz

Mini Project

Description

Build a small application configuration value with nested server and proxy settings. This mirrors how command-line tools and web services organize related configuration while keeping field access clear.

Goal

Create and print a configuration that contains a named nested ProxyConfig struct.

Requirements

Define a ProxyConfig type with Address and Port fields. Define an AppConfig type with Name and Proxy fields. Initialize both the outer and nested structs using keyed fields. Print the application name and complete proxy endpoint. Use a named type for the nested proxy settings.

Take Quiz

Keep learning

Related questions

View more