Packages & visibility
PHP organises code with namespaces, classes and the PSR-4 autoloader, and controls access with public, protected and private on every property and method. Go replaces all of that with two rules. A package is a directory, and a name that starts with a capital letter is visible outside it.
A package is a directory
Every .go file starts with package something, and every file in one directory must say the same thing. Together they're one package. Split it across ten files or one: to Go, it's the same package.
Files in a package never import each other. They simply share everything: every function, type, constant and variable, in any file, is visible to all the others. There's no file-level privacy, and no require.
To use another package, you import it by its import path: the module path from go.mod (lesson 0.4) followed by the directory.
import "example.com/tube/board" board.Row(d) // the last part of the path is the name
flowchart TB accTitle: package main importing package board accDescr: package main, in the module root, imports example.com/tube/board. That lets it use board's exported names, Row and Width. board's lower-case names, pad and filler, stay invisible to main. m["package main"] m -->|"can use"| ex["board.Row, board.Width<br/>exported"] m -.-x|"can't see"| pr["board.pad, board.filler<br/>private to board"]
There's no autoloader and no use statement doing the work behind the scenes. Every package a file uses is listed at the top of that file, and an unused one doesn't compile (0.2).
Capital letters are public
package board const Width = 12 // exported const filler = " " // private to board func Row(dest string) string { /* ... */ } func pad(s string) string { /* ... */ }
That's the whole visibility system. A name that starts with an upper-case letter is exported, so other packages can use it as board.Row. A lower-case name is visible only inside its own package. The rule applies to everything with a name: functions, types, constants, variables, struct fields and, in Unit 3, methods.
Mind the gap
"Private" means private to the package, not to a type. PHP's private hides a property from everything outside its class. In Go, any code in the same package can read a lower-case field. And Go has no protected at all, because there's no inheritance to protect things from (3.4). If something must be hidden from the rest of the package, it needs a package of its own.
Fields follow the same rule, and this is the one that catches people. A struct called Departure can be exported while its dest field isn't. Code outside the package then gets unknown field Dest in struct literal of type board.Departure, but does have unexported dest. The standard library plays by the same rules, so encoding/json silently skips lower-case fields as well. You'll meet that in 7.3.
Go code tends not to write getters for plain data. If a field should be readable outside the package, export it. Methods that guard a value, with rules about what it may hold, come in Unit 3.
Naming a package
Package names are short, lower case and one word: board, strconv, http. No underscores, no Utils, no Helpers.
| PHP | Board\BoardService::boardRow() |
|---|---|
| Go, week one | board.BoardRow() |
| Go, month two | board.Row() |
Callers always write the package name in front, so don't repeat it in what you export. board.BoardRow stutters. board.Row reads properly. If you're used to App\Services\Board\BoardService, this takes some unlearning, but your call sites get much shorter.
Symfony corner
Symfony's autowiring scans src/, registers each class as a service and injects them by type, so a controller can ask for a BoardFormatter and simply get one. Go has no container and no scanning. A package is used only when something imports it, and main builds whatever each part needs and passes it in, in plain code, as 0.2 promised.
Tip
No import cycles. If board imports timetable, then timetable can't import board, directly or by a longer route. The build fails with import cycle not allowed. PHP's autoloader never minded. When you hit this, it usually means the shared piece belongs in a third package that both can import. Lesson 5.1 covers project layout, including the special internal/ directory.
Recap
- A package is every
.gofile in one directory. Files in a package share everything, without imports. - Import by path, the module path plus the directory, and refer to the package by its last part:
board.Row. - A capital first letter exports a name. Lower case is private to the package. There's no
protected. - Struct fields follow the same rule, and lower-case fields are invisible to other packages,
encoding/jsonincluded. - Short package names, no stutter, and no import cycles.