mirror of
https://github.com/golang/go
synced 2024-11-25 16:57:58 -07:00
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:
parent
793cbd5b81
commit
f8284b64ce
@ -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
12
doc/progs/unused1.go
Normal file
@ -0,0 +1,12 @@
|
||||
// skip
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func main() {
|
||||
greeting := "hello, world"
|
||||
}
|
16
doc/progs/unused2.go
Normal file
16
doc/progs/unused2.go
Normal file
@ -0,0 +1,16 @@
|
||||
// compile
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
var _ = fmt.Printf
|
||||
var _ io.Reader
|
||||
|
||||
func main() {
|
||||
greeting := "hello, world"
|
||||
_ = greeting
|
||||
}
|
Loading…
Reference in New Issue
Block a user