--
Go defines a special identifier _
, called the blank identifier. The blank identifier can be used in a declaration to avoid declaring a name, and it can be used in an assignment to discard a value. This definition makes it useful in a variety of contexts.
If an assignment requires multiple values on the left side, but one of the values will not be used by the program, using the blank identifier in the assignment avoids the need to create a dummy variable. We saw one example of this in the discussion of for loops above.
sum := 0 for _, value := range array { sum += value }
Another common use is when calling a function that returns a value and an error, but only the error is important.
if _, err := os.Stat(path); os.IsNotExist(err) { fmt.Printf("%s does not exist\n", path) }
A final use that is more common than it should be is to discard the error from a function that is not expected to fail. This is usually a mistake: when the function does fail, the code will continue on and probably panic dereferencing a nil pointer.
// Always check errors: this program crashes if path does not exist. fi, _ := os.Stat(path) fmt.Printf("%s is %d bytes\n", path, fi.Size())
Go defines that it is an error to import a package without using it, or to declare a variable without using its value. Unused imports bloat a program and lengthen compiles unnecessarily; a variable that is initialized but not used is at least a wasted computation and perhaps indicative of a larger bug. Of course, both of these situations also arise in programs that are under active development, as you test and refine your code.
For example, in this program, there are two unused imports (fmt
and io
) and an unused variable (greeting
).
package main import ( "fmt" "io" ) func main() { greeting := "hello, world" }
Top-level blank declarations referring to the packages will silence the unused import errors. By convention, these declarations should come immediately after the imports, as a reminder to clean things up later. Similarly, assigning greeting
to a blank identifier will silence the unused variable error.
package main import ( "fmt" "io" ) var _ = fmt.Printf var _ io.Reader func main() { greeting := "hello, world" _ = greeting }
An unused import like fmt
or io
in the last section should eventually be used or removed: blank assignments identify code as a work in progress. But sometimes it is useful to import a package only for its side effects, without any explicit use. For example, during its init
function, the net/http/pprof
package registers HTTP handlers that provide useful debugging information. It has an exported API too, but most clients need only the handler registration. In this situation, it is conventional to rename the package to the blank identifier:
import _ "net/http/pprof"
This form of import makes clear that the package is being imported for its side effects, because there is no other possible use of the package: in this file, it doesn't have a name.
As we saw in the discussion of interfaces above, Go does not require a type to declare explicitly that it implements an interface. It implements the interface by simply implementing the required methods. This makes Go programs more lightweight and flexible, and it can avoid unnecessary dependencies between packages. Most interface conversions are static, visible to the compiler, and therefore checked at compile time. For example, passing an *os.File
to a function expecting an io.Reader
will not compile unless *os.File
implements the io.Reader
interface.
However, some types that are used only to satisfy dynamic interface checks. For example, the encoding/json
package defines a Marshaler
interface. If the JSON encoder encounters a type implementing that interface, the encoder will let the type convert itself to JSON instead of using the standard conversion. This check is done only at runtime, with code like:
m, ok := val.(json.Marshaler)
If a type—for example, json.RawMessage
—intends to customize its JSON representation, it should implement json.Marshaler
, but there are no static conversions that would cause the compiler to verify this automatically. A declaration can be used to add such a check:
var _ json.Marshaler = (*MyMessage)(nil)
As part of type-checking this static assignment of a *RawMessage
to a Marshaler
, the Go compiler will require that *RawMessage
implements Marshaler
. Using the blank identifier here indicates that the declaration exists only for the type checking, not to create a variable. Conventionally, such declarations are used only when there are no static conversions already present in the code.
Interesting. Which keystrokes do you use to do this in GoSublime?
On Friday, January 18, 2013 1:18:51 AM UTC-8, kortschak wrote:In addition to the techniques Rémy suggests, GoSublime makes adding and removing imports often as little as 6 key strokes away without any in file navigation.
--
What I took from the blog post is that we have not done a good job of explaining how to silence the errors using the blank identifier. The next version of Effective Go will contain a section something like the text below.
A few days ago I noticed the part of the spec about blank-namedmethods: it is legal to say:
type T int
func (T) _() {}
func (T) _(int) {}
func (T) _() int {return 2}
func (T) _(x int) int { return x }
in the same program.
Is it there for consistency or did someone find an actual use of that?
Russ, do you mean that the below is for the "next version" of http://golang.org/doc/effective_go.html?
On Tue, Jan 22, 2013 at 2:42 PM, Jan Mercl <0xj...@gmail.com> wrote:If the scope you're using at compile-time has unused unexported
> But the package scope is not static. Some build system/tag, script,
> code generator, ... can actually use the function which looks like
> unused only at this time, only at this builder run, only at this
> architecture/platform, ...
functions, it could indicate that the function is defined in a
sub-optimal location / scope. For example, if func foo() lives in
bar.go, but is only called from somewhere in bar_windows.go, it'd be
better to move foo() out of bar.go and into bar_windows.go. I'm not
saying it should necessarily be treated as fatal at compile time, but
I think detection of such things would still be fairly useful.
Somewhat unrelated tangent: I doubt Go will ever be used in a system
where safety is critical. Garbage collection and some of its intended
randomness (scheduling, map iteration, etc.) would basically
disqualify it.
I would just like to pipe in that I think Russ's suggestion for editing effective go is a good one, and also that it's probably a good idea to say something about it in the tour of go. I remember when I first started to write in go, even simple programs were tough at first because there were a bunch of compile errors that were unexpected, mainly due to unused variables / imports