What is "_," (underscore comma) in a Go declaration?
Categories:
Understanding the Underscore-Comma ( _, ) in Go Declarations

Explore the purpose and common use cases of the underscore-comma syntax in Go, a powerful idiom for ignoring unwanted values.
In Go programming, you'll often encounter a peculiar syntax in variable declarations or function calls: the underscore followed by a comma (_,
). This seemingly simple construct plays a crucial role in Go's philosophy of explicit error handling and unused variable prevention. This article will demystify the _,
idiom, explaining its purpose, common applications, and why it's an essential part of writing idiomatic Go code.
The Purpose of the Blank Identifier
The underscore (_
) in Go is known as the blank identifier. It's a special placeholder that can be used in contexts where a variable or package name is required but its value or identity is not needed. When used in a declaration with a comma, as in _, variable
, it specifically signals to the Go compiler that the value assigned to that position should be discarded. This is particularly useful when a function returns multiple values, but you only care about a subset of them.
package main
import (
"fmt"
"strconv"
)
func main() {
// Function returns two values: result and error
// We only care about the result, so we ignore the error
numStr := "123"
num, _ := strconv.Atoi(numStr)
fmt.Printf("Converted number: %d\n", num)
// Example where we ignore the first return value
_, err := fmt.Println("Hello, Go!")
if err != nil {
fmt.Printf("Error printing: %v\n", err)
}
}
Ignoring return values with the blank identifier
_
for unused return values is not just a convention; it's a requirement. Go's compiler will produce an error if you declare a variable and don't use it. The blank identifier explicitly tells the compiler to ignore the value, thus preventing such errors.Common Use Cases for _,
The _,
syntax is prevalent in several scenarios within Go programming. Understanding these contexts will help you write cleaner and more efficient code. The primary use cases involve ignoring unwanted return values from functions, particularly errors when you're certain they won't occur or can't be handled, and iterating over collections where only the value or index is needed.
flowchart TD A[Function Call Returns Multiple Values] --> B{Do I need all values?} B -->|Yes| C[Assign to named variables] B -->|No| D{Which values are not needed?} D --> E[Use `_` for unneeded values] E --> F[Example: `value, _ := funcCall()`] E --> G[Example: `_, err := funcCall()`] C --> H[Continue program flow] F --> H G --> H
Decision flow for using the blank identifier in multi-value returns
Ignoring Errors
One of the most frequent applications of _,
is to ignore the error
return value from a function. While Go encourages explicit error handling, there are situations where you might intentionally choose to ignore an error. This could be because you're confident the operation will not fail in a specific context, or because handling the error is not critical for the program's logic at that point. However, it's crucial to use this judiciously, as ignoring errors can lead to subtle bugs if not done carefully.
package main
import (
"fmt"
"os"
)
func main() {
// os.Stdout.Write returns (n int, err error)
// We only care about the number of bytes written, not the error
// in this simple case, as writing to stdout rarely fails.
n, _ := os.Stdout.Write([]byte("Writing directly to stdout\n"))
fmt.Printf("Wrote %d bytes.\n", n)
// Another example: parsing an integer where we expect valid input
// and are not concerned with parsing errors for this specific logic.
// (In real-world code, you'd usually check the error here.)
val := "100"
num, _ := fmt.Sscanf(val, "%d", &n)
fmt.Printf("Scanned %d items, value is %d.\n", num, n)
}
Ignoring error return values
Ignoring Loop Variables
When iterating over maps or slices using a for...range
loop, Go provides both the index/key and the value. If you only need one of these, the blank identifier comes in handy. For example, if you only need the values from a slice, you can ignore the index. Similarly, if you only need the keys from a map, you can ignore the value.
package main
import "fmt"
func main() {
// Iterating over a slice, ignoring the index
slice := []string{"apple", "banana", "cherry"}
fmt.Println("Slice values:")
for _, fruit := range slice {
fmt.Printf("- %s\n", fruit)
}
// Iterating over a map, ignoring the value
myMap := map[string]int{"one": 1, "two": 2, "three": 3}
fmt.Println("Map keys:")
for key, _ := range myMap {
fmt.Printf("- %s\n", key)
}
}
Ignoring loop variables with the blank identifier