Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

Promoted fields and methods in Go are members belonging to an anonymous (embedded) type—which can be a struct, a non-struct named type, or an interface—that are directly accessible from the enclosing parent struct as if they were declared directly on the parent struct. This mechanism is the direct result of type embedding. When a type is declared as an anonymous field within a struct, the compiler implicitly creates a field whose name is the unqualified type name. The fields and methods of that embedded type are then “promoted” to the parent struct.

Syntax and Mechanics

To promote fields, declare a field in a struct using only the type name, omitting the explicit field identifier.
type Inner struct {
    Value int
}

type Outer struct {
    Inner  // Anonymous field; promotes Inner's fields to Outer
    Name   string
}

func main() {
    o := Outer{}

    // Direct access to the promoted field
    o.Value = 42 

    // Fully qualified access (equivalent to the above)
    o.Inner.Value = 42 
    
    _ = o // Prevent 'declared and not used' compile error
}
Once embedded, the fields of Inner can be accessed directly on an instance of Outer using standard dot notation, bypassing the implicit Inner field.

Visibility and Export Rules

Promoted fields and methods strictly adhere to Go’s standard visibility and export rules. They retain their original export status from the embedded type. If a parent struct embeds a type imported from an external package, any unexported fields or methods (those starting with a lowercase letter) of the embedded type remain unexported. They are not accessible directly on the parent struct from outside the embedded type’s original package, even though they are technically promoted.

Composite Literal Restriction

While promoted fields can be accessed or assigned directly via dot notation after initialization, they cannot be used as field names within composite literals when initializing the parent struct. The parent struct must be initialized using the implicit name of the embedded type.
type Inner struct {
    Value int
}

type Outer struct {
    Inner
}

func main() {
    // COMPILE ERROR: unknown field Value in struct literal of type Outer
    // o := Outer{Value: 42} 

    // Valid initialization
    o := Outer{
        Inner: Inner{Value: 42},
    }
    
    _ = o // Prevent 'declared and not used' compile error
}

Shadowing and Name Resolution

Go resolves promoted fields using a breadth-first (level-order) search, stopping at the shallowest depth where a field or method name is found. If the parent struct declares a field with the exact same identifier as a field in the embedded type, the parent’s field shadows the promoted field. The embedded field is not overwritten or removed; it simply loses its promoted status and must be accessed via its fully qualified path.
type Base struct {
    ID int
}

type Container struct {
    Base
    ID int // Shadows Base.ID
}

func main() {
    c := Container{}
    c.ID = 1       // Assigns to Container.ID
    c.Base.ID = 2  // Assigns to the shadowed Base.ID
    
    _ = c // Prevent 'declared and not used' compile error
}

Collisions at the Same Depth

If a struct embeds multiple anonymous types that contain fields with identical names, a collision occurs at the same depth level. The Go compiler permits this declaration, but attempting to access the conflicting promoted field directly results in an ambiguous selector compile-time error. To resolve the ambiguity, the fully qualified path must be used.
type SourceA struct {
    Status string
}

type SourceB struct {
    Status string
}

type Aggregate struct {
    SourceA
    SourceB
}

func main() {
    agg := Aggregate{}
    
    // agg.Status = "OK" // COMPILE ERROR: ambiguous selector agg.Status
    
    agg.SourceA.Status = "OK"  // Valid
    agg.SourceB.Status = "Err" // Valid
    
    _ = agg // Prevent 'declared and not used' compile error
}

Method Promotion and Method Sets

The promotion rules apply identically to methods, but the specific methods promoted to the parent struct’s method set depend on whether the embedded type is a value (T) or a pointer (*T), and whether the parent struct is evaluated as a value (S) or a pointer (*S). Given a parent struct S and an embedded type T:
  • Embedding a value type (T): The method set of S includes promoted methods with receiver T. The method set of *S includes promoted methods with receiver T and *T.
  • Embedding a pointer type (*T): The method sets of both S and *S include promoted methods with receiver T and *T.
In all cases, when a promoted method is called, the receiver of the method remains the embedded type, not the parent struct.
type CustomInt int

func (c CustomInt) IsPositive() bool {
    return c > 0
}

type Wrapper struct {
    CustomInt // Embedding a non-struct named type (value embedding)
}

func main() {
    w := Wrapper{
        CustomInt: 5,
    }
    
    // Promoted method call. The receiver is CustomInt, not Wrapper.
    _ = w.IsPositive() 
}

Pointer Embedding

Fields and methods are also promoted if the anonymous field is a pointer to a type. The compiler automatically handles the pointer indirection when accessing the promoted members. However, this introduces a critical safety consideration: if the embedded pointer is nil (which is its default zero value), attempting to access its promoted fields or methods will result in a runtime panic (invalid memory address or nil pointer dereference). The pointer must be explicitly initialized before its promoted members can be evaluated.
type Config struct {
    Timeout int
}

type Server struct {
    *Config // Pointer embedding
}

func main() {
    // Valid initialization and access
    s1 := Server{
        Config: &Config{Timeout: 30},
    }
    
    // Implicitly dereferences the Config pointer
    _ = s1.Timeout 

    // Uninitialized pointer embedding
    s2 := Server{}
    
    _ = s2 // Prevent 'declared and not used' compile error
    
    // RUNTIME PANIC: invalid memory address or nil pointer dereference
    // _ = s2.Timeout 
}
Master Go with Deep Grasping Methodology!Learn More