Visibility is decided by capitalisation
Go has no public, private or protected keywords. Whether a name can be used from other packages depends on its first letter:
- A name that starts with an uppercase letter is exported: code in any package can use it.
- A name that starts with a lowercase letter is unexported: only code in the same package can use it.
The rule applies to every package-level name: types, functions, variables, constants, and also the fields and methods of a struct. So Dog, NewDog and a field Name are public, while dog, newDog and a field name are private to the package.
Note the unit of privacy: it is the package (a directory of files compiled together), not the type and not the file. There is nothing like a Java private field that other code in the same package cannot touch. Code anywhere in the package can read and write an unexported field.
Why hide a field?
Hiding data protects the rules of a type. Take a bank account:
type BankAccount struct {
balance float64 // unexported
}
func (b *BankAccount) deposit(amount float64) {
if amount < 0 {
return
}
b.balance += amount
}
func (b BankAccount) getBalance() float64 { return b.balance }
If balance were exported as Balance, any client could write account.Balance = -1000 and bypass the check in deposit. With the field unexported, code outside the package has to go through the methods. The methods keep the object in a valid state, and the internals can change later without breaking clients.
What it looks like across packages
Suppose Dog lives in a package named kennel, with an unexported name field. Code in another package, such as main, sees this:
rex := kennel.NewDog("Rex")
fmt.Println(rex.name) // compile error: name is unexported
fmt.Println(rex.GetName()) // works, if GetName is exported
The lessons run as one package, so there you cannot see the error. Everything shares the package, and rex.name would compile. The exercise therefore practises the pattern rather than the compile error: expose a field through a method.
Exported methods and constructors
The same rule explains the constructor convention. NewDog starts with an uppercase letter so other packages can call it, while the struct's fields stay lowercase. Outside code builds a Dog through the constructor and reads it through exported methods, and it never touches the fields directly. This is how Go achieves encapsulation without keywords.
Getter naming
This lesson names its getter getName. In ordinary Go code a getter for a field name is called Name, without the get prefix, and a setter is SetName. Because the first letter is uppercase, that getter is also exported, which is the point if other packages are meant to call it. A method named getName is itself unexported, so in a real multi-package program no outside code could call it. Inside this lesson's single package, it works.
The lesson's exercise
The Dog struct has an unexported name field and a NewDog constructor. The task is a getter and a short program that uses it.
- Declare
getNameon aDogreceiver namedthis, returning astring. - Return
this.name. The method belongs toDog, so it may read the field. - Declare
mainFunctionwith no parameters. - Create
rexwithNewDog("Rex"), so the name is set in one place, and print the label joined torex.getName()with+, which concatenates strings.
type Dog struct {
name string
}
func NewDog(name string) Dog {
return Dog{name: name}
}
func (this Dog) getName() string {
return this.name
}
func mainFunction() {
rex := NewDog("Rex")
fmt.Println("Dog's name: " + rex.getName())
}
The output is:
Dog's name: Rex
Common mistakes
- Looking for a keyword. Writing
private name stringis a syntax error. Capitalisation is the only switch. - Exporting the field by accident. Capitalising it, as in
Name, lets every package change it directly. - Assuming same-package code is blocked. Unexported means package-private, not type-private.
- Mixing a string and a number with
+."Age: " + 4does not compile. Usefmt.Sprintforstrconv.Itoa. - Forgetting the return type. A getter declared without
stringafter its parentheses cannot return a value.
Key takeaways
- Uppercase first letter means exported, lowercase means package-private.
- This applies to types, functions, fields and methods.
- Unexported fields plus methods let a type enforce its own rules.
- The usual Go getter is
Name(), notgetName().