0
0
GoComparisonBeginner · 3 min read

Exported vs Unexported in Go: Key Differences and Usage

In Go, exported identifiers start with an uppercase letter and are accessible from other packages, while unexported identifiers start with a lowercase letter and are only accessible within the same package. Use exported names to share functionality publicly and unexported names to keep details private within a package.
⚖️

Quick Comparison

This table summarizes the main differences between exported and unexported identifiers in Go.

AspectExportedUnexported
NamingStarts with uppercase letter (e.g., MyFunc)Starts with lowercase letter (e.g., myFunc)
VisibilityAccessible from other packagesAccessible only within the same package
Use casePublic API or shared functionalityInternal implementation details
Access from other packagesAllowedNot allowed
ConventionIndicates public useIndicates private use
⚖️

Key Differences

In Go, whether an identifier is exported or unexported depends solely on the first letter of its name. If it starts with an uppercase letter, it is exported and can be accessed from other packages. This is how Go controls visibility without explicit keywords like public or private.

Unexported identifiers start with a lowercase letter and are only visible inside the package where they are declared. This allows you to hide implementation details and keep your package's internal workings private, exposing only what you want users to see.

Using exported identifiers is essential when you want to create a public API for your package, while unexported identifiers help maintain encapsulation and reduce accidental misuse from outside code.

⚖️

Code Comparison

Here is an example showing an exported function and variable in a package.

go
package main

import (
	"fmt"
)

// Exported variable (accessible outside package)
var ExportedVar = "I am exported"

// unexported variable (only accessible inside this package)
var unexportedVar = "I am unexported"

// Exported function
func ExportedFunc() {
	fmt.Println("This is an exported function")
}

// unexported function
func unexportedFunc() {
	fmt.Println("This is an unexported function")
}

func main() {
	fmt.Println(ExportedVar)    // Works
	fmt.Println(unexportedVar)  // Works inside same package
	ExportedFunc()              // Works
	unexportedFunc()            // Works inside same package
}
Output
I am exported I am unexported This is an exported function This is an unexported function
↔️

Unexported Equivalent

Now imagine this code is in a different package trying to access the same identifiers.

go
package main

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

func main() {
	fmt.Println(mypkg.ExportedVar) // Works: exported
	// fmt.Println(mypkg.unexportedVar) // Error: unexportedVar not accessible
	mypkg.ExportedFunc()            // Works: exported
	// mypkg.unexportedFunc()         // Error: unexportedFunc not accessible
}
Output
I am exported This is an exported function
🎯

When to Use Which

Choose exported identifiers when you want to provide functionality or data that other packages should use. This is how you create a public interface for your package.

Choose unexported identifiers to hide implementation details and keep your package's internal logic private. This helps prevent misuse and makes your code easier to maintain.

In short, export only what is necessary and keep everything else unexported to maintain clean and safe code boundaries.

Key Takeaways

Exported identifiers start with uppercase letters and are accessible from other packages.
Unexported identifiers start with lowercase letters and are private to their package.
Use exported names to share public API and unexported names to hide internal details.
Go uses naming conventions, not keywords, to control visibility.
Keep your package interface minimal by exporting only what is needed.