Fix Go "declared and not used" and "imported and not used"

Deepak Prasad
Tested on RHEL 10.2 with Go 1.26.5
Package go 1.26.5
Applies to Any host with Go installed
Privilege Normal user
Scope Fix Go compiler errors for unused local variables and unused imports: delete or use bindings, blank identifiers for intentional discards, blank imports for init side effects, range-loop indexes, and := shadowing in branches. Does not cover full variable-scope tutorials or linter configuration.
Related guides Variable scope in Go
Golang if else
Return multiple values in Go
Getting started with Go
Golang iterate over array

Go rejects unused local variables and unused imports at compile time. Normally you remove the dead declaration or actually use the value. Use _ only when you intentionally need to discard a result or import a package for its init side effects.

Typical compiler messages:

text
x declared and not used
"fmt" imported and not used

Why Go reports "declared and not used"

The Go compiler rejects unused variables declared inside function bodies (including short declarations with :=). If you declare count := 0 and never read count, go build stops with count declared and not used. Package-level variables and function parameters follow different rules—unused parameters in a signature do not trigger this message.

This is a compiler error, not a go vet warning. There is no -gcflags or build tag that disables it—the fix is to change the code.


Fix an unused variable

Work through these in order:

  1. Delete the variable when it serves no purpose.
  2. Use the value in your logic or output.
  3. Discard an unwanted result with _ when you must accept a return value but only care about another one.

A common discard pattern is a range loop where you need the element, not the index:

go
for _, value := range values {
	fmt.Println(value)
}

For multiple return values, keep the results you need:

go
line, err := reader.ReadString('\n')
if err != nil {
	return err
}
fmt.Println(line)

Do not drop err just to satisfy the compiler. _ = variable can silence a binding temporarily while refactoring, but deleting dead code is usually the better permanent fix.


Fix "imported and not used"

If fmt is imported but no identifier from fmt appears in the file, you see:

text
"fmt" imported and not used

Remove the import line when you added it by mistake or finished refactoring.

A blank import imports the package solely for its initialization side effects; its package name is not available for normal references. Database drivers are a common case:

go
import (
	"database/sql"

	_ "github.com/mattn/go-sqlite3"
)

The _ means "import for side effects only." It is not a generic way to hide imports you forgot to use—if you do not need the package's init behavior, delete the import instead.

goimports removes unreferenced imports and adds missing ones. Editors using gopls can usually be configured to organize imports automatically on save or format. Install goimports with go install golang.org/x/tools/cmd/goimports@latest if your editor does not already run it. goimports can fix the import list automatically, but it will not decide how an unused local variable should be handled; delete or use that variable yourself.


Ignore unused indexes and return values

Range loops: for i, value := range values declares i even when you only use value. Either use i or replace it with _:

go
for _, value := range values {
	process(value)
}

Multiple returns: when you need only one result from a function that returns several non-error values, bind the rest to _ on the left:

go
key, _, ok := strings.Cut(line, "=")
if !ok {
	return fmt.Errorf("missing separator")
}

Prefer handling err explicitly in value, err := patterns. Use _ for a return you genuinely do not need, not to ignore errors.


Fix := shadowing and redeclaration mistakes

:= inside an inner block can declare a new variable with the same name as an outer one. If that inner variable is never read, you get declared and not used. When the inner variable is used, the code compiles—but the outer binding can still keep an unexpected value because the inner name shadows it. The Go spec allows an identifier declared in an inner block to shadow one from an outer block.

In the same block, := does not always create every name from scratch: it may redeclare existing variables when at least one non-blank variable on the left is new. For example, value, err := getValue() can reuse an existing value if err is new in that block.

Assign to the existing variable with = when you mean to update it:

go
var value string

if condition {
	value = "yes"
} else {
	value = "no"
}
fmt.Println(value)

Accidentally using short declaration in the branch creates an inner value that shadows the outer one:

go
var value string
if condition {
	value := "yes"
	fmt.Println("inside:", value)
}
fmt.Println("outside:", value)

This compiles, but outside: prints an empty string because the outer value was never assigned. For more on block scope, see variable scope in Go.


Summary

declared and not used means a local binding is never read; imported and not used means a package import has no references. Delete dead code first, use _ to discard a result you intentionally ignore (such as a range index), and use _ "path" only for documented init side effects. Inside branches, prefer = on an existing name over := when you are not introducing a new variable. There is no compiler switch to disable these checks.


References


Frequently Asked Questions

1. Can I disable "declared and not used" in Go?

No. It is a compiler error, not a linter warning, and there is no flag to turn it off. Remove the unused binding, use it, or discard it intentionally with the blank identifier.

2. When should I use _ = variable?

Rarely and usually only while refactoring. Prefer deleting dead code or using the value. A blank identifier on the left side of an assignment is the normal way to discard one result from a multi-value expression.

3. Why does a range loop cause "declared and not used"?

A two-value range gives index and element. If you declare both but only use the element, the index triggers the error. Use for _, value := range items when you do not need the index.

4. What is the difference between _ = value and _ "package"?

_ = value discards a single expression result. _ "package/path" imports the package solely for initialization side effects; its package name is not available for normal references, such as driver registration.

5. Why does := create another variable inside an if block?

A short declaration with := must declare at least one new non-blank variable in the current block. Inside an inner block, value := "yes" declares a new inner value that shadows the outer one; use value = "yes" to update the existing variable. Existing variables may be redeclared only when they were declared earlier in the same block, have the same type, and at least one non-blank variable is new.

6. Do unused function parameters cause this compiler error?

No. Unused named parameters in a function signature are allowed. The declared and not used error applies to local variables inside the function body. Rename a parameter to _ only when you want to signal intentionally that the argument is ignored.
Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise spanning these technologies, he develops robust solutions and implements efficient data processing and management strategies across various projects and platforms.

  • Go (programming language)
  • Python (programming language)
  • Java (programming language)
  • MongoDB
  • Kubernetes