doc/effective_go.html: add a section about the blank identifier

R=golang-dev, minux.ma, bradfitz, adg
CC=golang-dev
https://golang.org/cl/7134056
This commit is contained in:
Russ Cox 2013-01-22 14:00:10 -05:00
parent 793cbd5b81
commit f8284b64ce
3 changed files with 178 additions and 0 deletions

View File

@ -2966,6 +2966,156 @@ filter unexpected problems and re-panic with the original error.
That's left as an exercise for the reader.
</p>
<h2 id="blank">Blank identifier</h2>
<p>
Go defines a special identifier <code>_</code>, called the <i>blank identifier</i>.
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.
</p>
<h3 id="blank_assign">Multiple assignment</h3>
<p>
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
<a href="#for">for loops</a> above.
</p>
<pre>
sum := 0
for _, value := range array {
sum += value
}
</pre>
<p>
Another common use is when calling a function that returns
a value and an error, but only the error is important.
</p>
<pre>
if _, err := os.Stat(path); os.IsNotExist(err) {
fmt.Printf("%s does not exist\n", path)
}
</pre>
<p>
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.
</p>
<pre>
// 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())
</pre>
<h3 id="blank_unused">Unused imports and variables</h3>
<p>
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.
</p>
<p>
For example, in this program, there are two unused imports
(<code>fmt</code> and <code>io</code>)
and an unused variable (<code>greeting</code>).
</p>
{{code "/doc/progs/unused1.go" `/package/` `$`}}
<p>
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 <code>greeting</code> to a blank identifier
will silence the unused variable error.
</p>
{{code "/doc/progs/unused2.go" `/package/` `$`}}
<p>
<h3 id="blank_import">Import for side effect</h3>
<p>
An unused import like <code>fmt</code> or <code>io</code> 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 <code>init</code> function,
the <code><a href="/pkg/net/http/pprof/">net/http/pprof</a></code>
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:
</p>
<pre>
import _ "net/http/pprof"
</pre>
<p>
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.
</p>
<h3 id="blank_implements">Interface checks</h3>
<p>
As we saw in the discussion of <a href="#interfaces_and_types">interfaces</a> 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 <code>*os.File</code> to a function
expecting an <code>io.Reader</code> will not compile unless
<code>*os.File</code> implements the <code>io.Reader</code> interface.
</p>
<p>
However, some types that are used only to satisfy dynamic interface checks.
For example, the <code><a href="/pkg/encoding/json/">encoding/json</a></code>
package defines a <code><a href="/pkg/encoding/json/#Marshaler">Marshaler</a></code>
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:
</p>
<pre>
m, ok := val.(json.Marshaler)
</pre>
<p>
If a type—for example,
<code><a href="/pkg/encoding/json/#RawMessage">json.RawMessage</a></code>—intends
to customize its JSON representation, it should implement
<code>json.Marshaler</code>, 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:
</p>
<pre>
var _ json.Marshaler = (*MyMessage)(nil)
</pre>
<p>
As part of type-checking this static assignment of a
<code>*RawMessage</code> to a <code>Marshaler</code>,
the Go compiler will require that <code>*RawMessage</code> implements <code>Marshaler</code>.
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.
</p>
<h2 id="web_server">A web server</h2>

12
doc/progs/unused1.go Normal file
View File

@ -0,0 +1,12 @@
// skip
package main
import (
"fmt"
"io"
)
func main() {
greeting := "hello, world"
}

16
doc/progs/unused2.go Normal file
View File

@ -0,0 +1,16 @@
// compile
package main
import (
"fmt"
"io"
)
var _ = fmt.Printf
var _ io.Reader
func main() {
greeting := "hello, world"
_ = greeting
}