Go > Packages and Modules > Creating Packages > Exported identifiers
Exported Identifiers in Go Packages
This snippet demonstrates how to create and use exported identifiers (variables, functions, types) in Go packages. Exported identifiers are accessible from outside the package, while unexported ones are only accessible within the package.
Creating a Go Package
This code defines a package named `mypackage`. It includes an exported variable `ExportedVariable`, an unexported variable `unexportedVariable`, an exported function `ExportedFunction`, and an unexported function `unexportedFunction`. It also includes an exported struct `ExportedStruct` with an exported field `ExportedField` and an unexported field `unexportedField`. The function `NewExportedStruct` is a common pattern to initialize struct and return it.
// mypackage/mypackage.go
package mypackage
import "fmt"
// ExportedVariable is accessible from outside the package.
var ExportedVariable = "Hello from mypackage"
// unexportedVariable is only accessible within the package.
var unexportedVariable = "This is private"
// ExportedFunction is accessible from outside the package.
func ExportedFunction() {
fmt.Println(ExportedVariable)
fmt.Println(unexportedFunction())
}
// unexportedFunction is only accessible within the package.
func unexportedFunction() string {
return unexportedVariable
}
// ExportedStruct is accessible from outside the package.
type ExportedStruct struct {
// ExportedField is accessible from outside the package.
ExportedField string
// unexportedField is only accessible within the package.
unexportedField string
}
// NewExportedStruct is a constructor function for ExportedStruct.
func NewExportedStruct(exported string, unexported string) *ExportedStruct {
return &ExportedStruct{ExportedField: exported, unexportedField: unexported}
}
// ExportedMethod is accessible from outside the package.
func (e *ExportedStruct) ExportedMethod() string {
return e.ExportedField
}
Using the Package
This code imports the `mypackage` package and uses its exported identifiers. Note that you can access `ExportedVariable`, `ExportedFunction`, `ExportedStruct` and `ExportedField` because they are exported (start with a capital letter). Attempting to access `unexportedVariable` or `unexportedField` directly will result in a compile-time error because they are unexported.
// main.go
package main
import (
"fmt"
"mypackage"
)
func main() {
fmt.Println(mypackage.ExportedVariable)
mypackage.ExportedFunction()
myStruct := mypackage.NewExportedStruct("Public Value", "Private Value")
fmt.Println(myStruct.ExportedField)
fmt.Println(myStruct.ExportedMethod())
//fmt.Println(myStruct.unexportedField) // This will cause a compile error
}
Concepts Behind Exported Identifiers
In Go, visibility is controlled by the case of the first letter of an identifier (variable, function, type, constant, field or method name). Identifiers that start with an uppercase letter are exported, meaning they are accessible from other packages. Identifiers that start with a lowercase letter are unexported, meaning they are only accessible within the package where they are defined. This is Go's mechanism for encapsulation and information hiding.
Real-Life Use Case
Consider a library for handling HTTP requests. You might export functions like `Get`, `Post`, `Put`, and `Delete`, along with types like `Request` and `Response`. You would likely keep internal details about request creation and connection management unexported. This prevents users from directly manipulating internal state and allows you to refactor the library's internals without breaking external code. Using exported identifiers helps to create well-defined and stable APIs for your package.
Best Practices
Interview Tip
Be prepared to explain the difference between exported and unexported identifiers in Go. Also, be prepared to discuss why you would choose to export or unexport a particular identifier. A good answer will demonstrate an understanding of encapsulation, API design, and maintainability.
When to use them
Use exported identifiers when you want to provide functionality to other packages. Unexported identifiers should be used for internal implementation details that should not be directly accessed or modified by external code. It's a core concept of encapsulation in Go.
Alternatives
There aren't really alternatives to exported identifiers, as they are fundamental to Go's package system and visibility control. The choice is always whether a given identifier should be exported or unexported, depending on your package's design goals. You can use interfaces to provide a more abstract layer on exported types.
Pros
Cons
FAQ
-
What happens if I try to access an unexported identifier from another package?
You will get a compile-time error. The compiler will report that the identifier is not defined or not exported. -
Can I change an unexported identifier to an exported identifier after the package is already in use?
Yes, but this is considered a breaking change. Clients of your package will now be able to access the identifier, which might introduce unintended dependencies or behavior. It's generally best to avoid making such changes unless absolutely necessary, and to communicate the change clearly to users of your package. -
Are constants also subject to export rules?
Yes, constants follow the same export rules as variables, functions, and types. Constants that start with an uppercase letter are exported, and constants that start with a lowercase letter are unexported.