Question
I'm learning Go, and I find it frustrating that the compiler does not allow unused variables or imported packages.
This slows me down during development. For example, I may want to import a package now and use it later, or temporarily comment out code while testing something. In these cases, the compiler reports errors such as variables being declared but not used.
Is there a way to disable or avoid this check in Go?
Short Answer
By the end of this page, you will understand why Go rejects unused variables and imports, why this rule exists, and how developers usually work with it in practice. You will also learn the correct ways to temporarily silence these errors during development without fighting the language.
Concept
In Go, unused local variables and unused imports are compile-time errors. This is a deliberate language design choice.
The goal is to keep code clean, readable, and maintainable:
- Unused variables often mean incomplete logic, old code, or mistakes.
- Unused imports add noise and can hide what the file actually depends on.
- Failing early helps catch problems before they become harder to notice.
Unlike some languages that only show warnings, Go treats these cases as errors. That means you usually cannot compile code until they are resolved.
A few important details:
- Unused local variables cause an error.
- Unused imports cause an error.
- Package-level variables can behave differently depending on context, but in day-to-day Go code, the most common issue is with local variables and imports.
- There is no normal compiler flag to globally disable this rule for regular Go development.
This matters in real programming because Go prefers a strict, predictable workflow. The language encourages you to keep files in a compilable state as you work. Instead of disabling the check, Go developers usually use small temporary techniques to satisfy the compiler while iterating.
Common temporary approaches include:
- Assigning a variable to the blank identifier:
_ = value - Importing a package with the blank identifier for side effects only:
import _ "package/name" - Removing the import until you actually need it
- Commenting out the declaration itself, not just its usage
So the short answer is: No, Go does not provide a standard way to turn this check off. The normal approach is to write code in a way that keeps the compiler happy while you develop.
Mental Model
Think of the Go compiler like a tidy teammate reviewing your desk.
- If you put a tool on the desk and never touch it, your teammate asks: Why is this here?
- If you bring a book into the room and never open it, your teammate asks: Why did you bring it?
In Go:
- A variable is like a tool you said you needed.
- An import is like a book you brought into the file.
If you are not using them, Go assumes something is unfinished, accidental, or unnecessary.
The blank identifier _ is like telling the compiler: Yes, I know this exists, and I am intentionally discarding it for now.
Syntax and Examples
Core syntax
Unused variable causes an error
package main
import "fmt"
func main() {
name := "Alice"
fmt.Println("Hello")
}
name is declared but never used, so Go reports an error.
Temporary fix with the blank identifier
package main
import "fmt"
func main() {
name := "Alice"
_ = name
fmt.Println("Hello")
}
This tells the compiler that you are intentionally ignoring name.
Unused import causes an error
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println("Hello")
}
Here, strings is imported but never used.
Fix by removing the import
package main
import "fmt"
func main() {
fmt.Println("Hello")
}
Import for side effects only
Sometimes a package is imported only because its init() function does useful work.
import _ "github.com/lib/pq"
This is valid when you intentionally want the package's side effects but do not directly reference any exported names from it.
Beginner-friendly example
package main
import "fmt"
func main() {
message := "Testing later"
_ = message
fmt.Println("Program still compiles")
}
This is useful during development when you want to keep a variable for later without breaking compilation.
Step by Step Execution
Consider this example:
package main
import "fmt"
func main() {
count := 10
_ = count
fmt.Println("Done")
}
Step by step:
- The program starts in
main. count := 10creates a local variable namedcount._ = countreads the variable and assigns it to the blank identifier.- The blank identifier discards the value.
- Because
countwas used, the compiler is satisfied. fmt.Println("Done")printsDone.
If you remove this line:
_ = count
then the compiler sees that count was declared but never used, and compilation fails.
Now compare with an unused import:
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println("Done")
}
Step by step:
- The file imports
fmtandmath. fmt.Printlnuses thefmtpackage.- Nothing in the file uses
math. - The compiler reports that
mathis imported and not used. - To fix it, either remove
mathor actually use it.
Real World Use Cases
This rule appears constantly in everyday Go development.
Common situations
- Refactoring code: you remove one line and suddenly an import is no longer needed.
- Debugging: you create a temporary variable to inspect a value.
- Building a feature step by step: you add a package import before writing the logic that uses it.
- Commenting out code during testing: this may leave variables or imports behind.
Real examples
1. API handler development
You start writing a JSON API handler and import encoding/json, but you have not yet added the response logic. Go will reject the unused import until you use it or remove it.
2. Database setup
Some database drivers are intentionally imported for side effects:
import _ "github.com/lib/pq"
This is a valid and common pattern.
3. Temporary debugging
result := calculateTotal(items)
_ = result
This can be a temporary step while you are building the next part of the function.
4. Incremental coding
Many Go developers write small compilable steps instead of sketching large amounts of unfinished code in one file. This fits the language's strict compile rules.
Real Codebase Usage
In real Go codebases, developers usually do not try to disable unused checks. Instead, they adopt workflows and patterns that work with the compiler.
Common patterns
Guarded incremental development
Write a small piece, compile it, then add the next piece. This keeps the codebase stable and avoids piles of unused declarations.
Remove and re-add imports automatically
Developers often rely on tools such as goimports, which automatically removes unused imports and adds missing ones.
Temporary blank identifier usage
During refactoring, it is common to temporarily write:
_ = value
But this is usually short-lived, not something left in finished code.
Side-effect imports
Packages that register drivers, metrics exporters, or plugins may be imported like this:
import _ "example.com/plugin"
That is not a hack; it is an intentional Go pattern.
Validation and early cleanup
When developers delete logic, they also clean up:
- old imports
- temporary variables
- commented-out code that is no longer needed
Team practices
In shared projects, this rule is helpful because it:
- reduces dead code
- makes imports reflect actual dependencies
- keeps code reviews cleaner
- prevents forgotten debug leftovers
So although it may feel strict at first, many teams see it as a productivity benefit over time.
Common Mistakes
1. Expecting a compiler option to disable the check
Many beginners look for a flag to turn off unused variable or import errors.
go build -ignore-unused
That is not a normal Go option.
Avoid this mistake: assume the check is part of the language workflow and use the standard workarounds instead.
2. Keeping unused imports around "for later"
Broken example:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("hello")
}
time is unused.
Fix: remove it now and re-add it later, or let goimports manage it.
3. Confusing _ = value with real logic
userID := 42
_ = userID
This only silences the compiler. It does not perform useful work.
Avoid this mistake: treat _ = value as temporary unless discarding the value is truly intentional.
4. Misusing blank imports
Broken idea:
import _ "fmt"
This compiles, but it makes no sense if you actually want to call fmt.Println.
Fix: use a normal import when you need the package directly.
5. Commenting out usage but leaving declarations behind
name := "Alice"
// fmt.Println(name)
Now name is unused.
Fix options:
- comment out the declaration too
- restore the usage
- add
_ = nametemporarily
6. Leaving temporary silence code in production
config := loadConfig()
_ = config
If this remains in final code, it may hide incomplete work.
Fix: remove temporary placeholders before finishing the feature.
Comparisons
| Concept | What it does | Common use | Good choice when |
|---|---|---|---|
| Unused variable error | Fails if a local variable is never used | Prevents dead or incomplete code | You want stricter correctness |
| Unused import error | Fails if an imported package is never referenced | Keeps dependencies clear | You want clean file-level dependencies |
Blank identifier _ = value | Marks a value as intentionally ignored | Temporary development aid | You need code to compile briefly |
Blank import import _ "pkg" | Imports a package only for side effects | Driver registration, plugin setup | The package's init() is the goal |
| Removing the code | Deletes unused declarations/imports | Normal cleanup | You do not need the code yet |
_ = value vs import _ "pkg"
| Pattern | Used for | Example |
|---|---|---|
_ = value | Ignoring a variable or expression result | _ = count |
import _ "pkg" | Importing a package only for side effects | import _ "github.com/lib/pq" |
They both use _, but they solve different problems.
Go vs languages that allow warnings only
| Language style | Behavior |
|---|---|
| Go | Unused locals and imports are typically compile errors |
| Some other languages | Unused items may only produce warnings |
Go chooses stricter enforcement to keep codebases cleaner.
Cheat Sheet
Quick rules
- Unused local variables cause a compile error.
- Unused imports cause a compile error.
- There is no standard Go setting to disable this globally.
- Remove unused code when possible.
- Use
_ = variableas a temporary workaround. - Use
import _ "package/path"only for intentional side effects.
Common fixes
Silence an unused variable temporarily
x := 10
_ = x
Remove an unused import
import "fmt"
Side-effect import
import _ "github.com/lib/pq"
When to use which
- Need it later? Remove it now and re-add it later.
- Temporarily unfinished function? Use
_ = valuebriefly. - Need package registration via
init()? Use a blank import.
Warning signs
These usually mean incomplete code:
_ = config
_ = result
_ = user
Review them before finalizing your code.
Helpful tooling
go fmtformats code.goimportsformats code and fixes imports automatically.
FAQ
Can I disable the unused variable check in Go?
No, not in normal Go development. The language and toolchain are designed to reject unused local variables and imports.
Why does Go treat unused variables as errors instead of warnings?
Because unused values often indicate mistakes, unfinished refactoring, or dead code. Go prefers catching these early.
How do I temporarily keep a variable without using it?
Use the blank identifier:
_ = myVar
This tells the compiler you are intentionally ignoring it.
How do I keep an import that I only need for side effects?
Use a blank import:
import _ "some/package"
This is common for database drivers and plugin registration.
Is _ = value good practice?
As a temporary development step, yes. As a permanent habit, usually no unless discarding the value is intentional.
What tool helps with unused imports in Go?
goimports is commonly used. It can automatically add missing imports and remove unused ones.
Should I comment out code or delete it?
Usually delete it and rely on version control if needed later. Long commented-out sections often make code harder to read.
Mini Project
Description
Create a small Go program that simulates a work-in-progress command-line tool. The project demonstrates how unused variables and imports appear during development, and how to fix them in the Go way without disabling compiler checks.
Goal
Build a Go program that compiles cleanly while temporarily holding values for future use and using imports correctly.
Requirements
- Create a
mainfunction that prints a message to the console. - Declare at least one variable that is not part of the final output yet.
- Use the blank identifier to keep the program compiling.
- Include one normal import that is used.
- Add a commented example of a side-effect import for reference.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.