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.

An unexported identifier in Go is a name (such as a variable, constant, function, struct, interface, or method) that is inaccessible from outside its defining package. Go does not use access modifier keywords (like private or protected). Instead, an identifier is unexported if it meets either of two conditions: it is declared within a local block (such as a function body), or it is declared in the package block (or as a struct field/method) but its first character is a lowercase Unicode letter or the underscore character (_).

Syntax and Mechanics

To be exported, an identifier must be declared in the package block (or be a struct field or method) AND begin with an uppercase Unicode letter. All other identifiers are unexported. Note that the exact identifier _ is the blank identifier, while names like _internal are standard unexported identifiers that begin with the underscore character. Furthermore, identifiers declared inside a function are always unexported, even if they begin with an uppercase letter, because their lexical scope is restricted to the local block.
package core

// Unexported package-level struct (starts with lowercase)
type engine struct {
    // Exported field within an unexported struct
    Capacity int 
    // Unexported field (starts with lowercase)
    serialNumber string 
}

// Unexported package-level variable (starts with lowercase)
var defaultRetries = 3

// Unexported package-level function (starts with underscore character)
func _calculateHash(data []byte) string {
    // Unexported local variable (starts with uppercase, but scope is local)
    LocalHash := "hash_value"
    return LocalHash
}

Visibility Rules

  1. Lexical Scope: Unexported package-level identifiers are accessible by any source file belonging to the same package. Go’s access control is package-level, not file-level. An unexported variable declared in a.go can be read and modified in b.go if both share the same package declaration. Conversely, local variables are restricted strictly to their local block (e.g., the function body) and are never accessible outside of it.
  2. Struct Fields: Visibility applies independently to struct fields. An exported struct can contain unexported fields. External packages can instantiate the struct but cannot read, write, or initialize the unexported fields using struct literals.
  3. Methods and Interfaces: Unexported methods attached to an exported type cannot be invoked from outside the package. If an exported interface contains an unexported method, a type defined in an external package cannot implement that interface. This is because the external type cannot define the unexported method belonging to the interface’s original package.
  4. Type Aliases: If an exported type alias points to an unexported type, the type itself remains unexported, but the alias allows external packages to interact with it under the exported name’s constraints.

Cross-Package Behavior

When an external package imports a package containing unexported identifiers, the compiler prevents any direct reference to those identifiers.
package main

import (
    "fmt"
    "example.com/core"
)

func main() {
    // COMPILER ERROR: cannot refer to unexported name core.defaultRetries
    // fmt.Println(core.defaultRetries)

    // COMPILER ERROR: cannot refer to unexported name core._calculateHash
    // core._calculateHash([]byte("data"))
    
    // COMPILER ERROR: cannot refer to unexported name core.engine
    // e := core.engine{}
}

Reflection and Unexported Fields

The reflect package can inspect unexported fields of a struct, but it cannot mutate them. Attempting to set a value on an unexported field via reflection will result in a runtime panic.
package main

import (
    "reflect"
    "fmt"
)

type Data struct {
    secret string
}

func main() {
    d := Data{secret: "hidden"}
    v := reflect.ValueOf(&d).Elem()
    
    field := v.FieldByName("secret")
    
    // Can check if it exists and read its type
    fmt.Println(field.IsValid()) // true
    
    // PANIC: reflect: reflect.Value.SetString using value obtained using unexported field
    // field.SetString("new value") 
}
Master Go with Deep Grasping Methodology!Learn More