Imports¶
Every .go file declares which packages it uses with an import block. Imports sit between the package clause and the rest of the file.
The basic shape¶
The string "fmt" is an import path. It tells the compiler where to find the package — for standard-library packages this is just the package's name; for third-party packages it's a URL-shaped path that the module system resolves.
import "fmt" // standard library
import "net/http" // standard library subpackage
import "github.com/gorilla/mux" // third-party module
import "example.com/myapp/internal/db" // internal to your own module
Single vs grouped¶
Two equivalent forms. Use grouped when you have more than one — almost always.
vs
The grouped form is what gofmt and editors produce automatically. Standard convention is to keep std-lib imports in one block and external imports in a separate block, separated by a blank line:
Using imported identifiers¶
Inside the file you refer to exported names through the package's base name — usually the last segment of the import path:
Two non-obvious rules:
- The base name comes from the package's own
packagedeclaration, not from the import path. They are usually the same, but not always —package "yaml.v3"declares itself aspackage yaml, so you reference it asyaml.Marshal, notv3.Marshal. - Only exported names are visible. Capitalized identifiers cross package boundaries; lowercase ones don't. (See 02-basic-types.md for the capitalization rule.)
import "strings"
s := strings.ToUpper("go") // exported — works
s := strings.toupper("go") // compile error — lowercase, not exported
Renaming with an alias¶
You can give an imported package a local nickname by writing it before the path:
When to reach for it:
- Two imports would collide otherwise (e.g.
crypto/randandmath/rand). - The package's base name is awkward or long for the local file.
Don't alias just to shorten — fmt.Println is already short.
From Python: ≈
import math as m. Same idea, syntax flipped.
Blank import — _ for side effects only¶
A blank import compiles and links the package but doesn't bind any name in your file. You can't reference anything from it. The point is to run the package's init() function for its side effects:
import (
"database/sql"
_ "github.com/lib/pq" // registers the "postgres" driver
)
func main() {
db, err := sql.Open("postgres", connStr)
// ...
}
Common cases: database drivers, image-format decoders, profiling hooks.
import (
_ "image/png" // registers PNG decoder with image.Decode
_ "image/jpeg" // registers JPEG decoder
_ "net/http/pprof" // registers /debug/pprof/* handlers
)
From Python: there is no direct analog. Python doesn't have an "import for side effects but never name it" form — every
importintroduces a name.
Dot import — almost never use¶
import . "path" dumps all exported names from the package into the current file's namespace, so you can call them unqualified:
This is strongly discouraged in production code — it makes it impossible to tell where an identifier came from when reading a file. The one legitimate use is inside test files that need to reach deep into an _test package, and even that is rare.
From Python: ≈
from math import *. Same problem, same advice.
Unused imports are a compile error¶
import (
"fmt"
"os" // imported but never referenced
)
func main() {
fmt.Println("hi")
}
// compile error: "os" imported and not used
This is intentional — the language designers wanted import lists to stay accurate. There are two escape hatches:
- Blank import:
_ "os"if you genuinely want the side effects. - Suppress while editing:
_ = os.Getpidsomewhere in the file, temporarily. Usegoimportsor your editor's organize-imports command to clean these up before committing.
From Python: Python warns about unused imports through linters (
pyflakes,ruff) but never blocks compilation. Go elevates it to a hard error.
Import cycles are forbidden¶
If package a imports b, then b cannot import a directly or indirectly.
The compiler detects cycles transitively. If you hit one, the fix is usually:
- Extract the shared dependency into a third package.
- Move the conflicting code closer to its caller.
- Use an interface defined in the caller's package and have the callee implement it (Go's "accept interfaces, return concrete types" idiom).
Standard library imports — small tour¶
The most-used std-lib packages:
| Import | Common use |
|---|---|
"fmt" |
Formatted printing, Println, Printf, Sprintf. |
"strings" |
String manipulation (ToUpper, Split, Replace, Contains). |
"strconv" |
String ↔ number conversion. |
"errors" |
errors.New, errors.Is, errors.As. |
"os" |
Process/file system, env vars, os.Args. |
"io" |
Reader / Writer interfaces, Copy. |
"bufio" |
Buffered I/O, Scanner. |
"time" |
time.Now, time.Duration, timers. |
"net/http" |
HTTP client and server. |
"encoding/json" |
JSON marshal/unmarshal. |
"sync" |
Mutex, WaitGroup, Once. |
"context" |
Cancellation, deadlines, request-scoped values. |
"log" |
Simple logging; log/slog for structured logs. |
"reflect" |
Runtime type introspection. |
Browse the full list at pkg.go.dev/std.
Tooling¶
You almost never type imports by hand. goimports (and gopls, which most editors run on save) adds missing imports and removes unused ones automatically — see 08-additional-tools.md.
After editing, this leaves the import block clean and grouped correctly.
Quick reference¶
| Form | Effect |
|---|---|
import "path" |
Bind by the package's own name. |
import alias "path" |
Bind under alias. |
import _ "path" |
Run init()s, no name bound — for side effects. |
import . "path" |
Merge package's exports into this file's namespace. Avoid. |