1
0
mirror of https://github.com/golang/go synced 2024-11-25 09:07:58 -07:00

effective_go.html: move and rework the blank identifier section

Also rename the relevant examples and make sure the working one compiles.

R=golang-dev, bradfitz, adg, iant, rsc
CC=golang-dev
https://golang.org/cl/7597043
This commit is contained in:
Rob Pike 2013-03-08 10:41:20 -08:00
parent 3cdf8bae1a
commit 9e329a0d16
6 changed files with 238 additions and 182 deletions

View File

@ -695,6 +695,9 @@ for _, value := range array {
}
</pre>
<p>
The blank identifier has many uses, as described in <a href="#blank">a later section</a>.
<p>
For strings, the <code>range</code> does more work for you, breaking out individual
Unicode code points by parsing the UTF-8.
@ -802,6 +805,8 @@ func Compare(a, b []byte) int {
}
</pre>
<h2 id="type_switch">Type switch</h2>
<p>
A switch can also be used to discover the dynamic type of an interface
variable. Such a <em>type switch</em> uses the syntax of a type
@ -1556,11 +1561,8 @@ func offset(tz string) int {
</pre>
<p>
To test for presence in the map without worrying about the actual value,
you can use the blank identifier (<code>_</code>).
The blank identifier can be assigned or declared with any value of any type, with the
value discarded harmlessly; it's a bit like writing to the Unix <code>/dev/null</code> file.
For testing just presence in a map, use the blank
identifier in place of the usual variable for the value.
you can use the <a href="#blank">blank identifier</a> (<code>_</code>)
in place of the usual variable for the value.
</p>
<pre>
_, present := timeZone[tz]
@ -2312,6 +2314,196 @@ a channel, and a function, all because interfaces are just sets of
methods, which can be defined for (almost) any type.
</p>
<h2 id="blank">The blank identifier</h2>
<p>
We've mentioned the blank identifier a couple of times now, in the context of
<a href="#for"><code>for</code> <code>range</code> loops</a>
and <a href="#maps">maps</a>.
The blank identifier can be assigned or declared with any value of any type, with the
value discarded harmlessly.
It's a bit like writing to the Unix <code>/dev/null</code> file:
it represents a write-only value
to be used as a place-holder
where a variable is needed but the actual value is irrelevant.
It has uses beyond those we've seen already.
</p>
<h3 id="blank_assign">The blank identifier in multiple assignment</h3>
<p>
The use of a blank identifier in a <code>for</code> <code>range</code> loop is a
special case of a general situation: multiple assignment.
<p>
If an assignment requires multiple values on the left side,
but one of the values will not be used by the program,
a blank identifier on the left-hand-side of the
the assignment avoids the need
to create a dummy variable and makes it clear that the
value is to be discarded.
For instance, when calling a function that returns
a value and an error, but only the error is important,
use the blank identifier to discard the irrelevant value.
</p>
<pre>
if _, err := os.Stat(path); os.IsNotExist(err) {
fmt.Printf("%s does not exist\n", path)
}
</pre>
<p>
Occasionally you'll see code that discards the error value in order
to ignore the error; this is terrible practice. Always check error returns;
they're provided for a reason.
</p>
<pre>
// Bad! This code will crash if path does not exist.
fi, _ := os.Stat(path)
if fi.IsDir() {
fmt.Printf("%s is a directory\n", path)
}
</pre>
<h3 id="blank_unused">Unused imports and variables</h3>
<p>
It is an error to import a package or to declare a variable without using it.
Unused imports bloat the program and slow compilation,
while a variable that is initialized but not used is at least
a wasted computation and perhaps indicative of a
larger bug.
When a program is under active development, however,
unused imports and variables often arise and it can
be annoying to delete them just to have the compilation proceed,
only to have them be needed again later.
The blank identifier provides a workaround.
</p>
<p>
This half-written program is has two unused imports
(<code>fmt</code> and <code>io</code>)
and an unused variable (<code>fd</code>),
so it will not compile, but it would be nice to see if the
code so far is correct.
</p>
{{code "/doc/progs/eff_unused1.go" `/package/` `$`}}
<p>
To silence complaints about the unused imports, use a
blank identifier to refer to a symbol from the imported package.
Similarly, assigning the unused variable <code>fd</code>
to the blank identifier will silence the unused variable error.
This version of the program does compile.
</p>
{{code "/doc/progs/eff_unused2.go" `/package/` `$`}}
<p>
By convention, the global declarations to silence import errors
should come right after the imports and be commented,
both to make them easy to find and as a reminder to clean things up later.
</p>
<h3 id="blank_import">Import for side effect</h3>
<p>
An unused import like <code>fmt</code> or <code>io</code> in the
previous example 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
debugging information. It has an exported API, but
most clients need only the handler registration and
access the data through a web page.
To import the package only for its side effects, 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.
(If it did, and we didn't use that name, the compiler would reject the program.)
</p>
<h3 id="blank_implements">Interface checks</h3>
<p>
As we saw in the discussion of <a href="#interfaces_and_types">interfaces</a> above,
a type need not declare explicitly that it implements an interface.
Instead, a type implements the interface just by implementing the interface's methods.
In practice, most interface conversions are static 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>
Some interface checks do happen at run-time, though.
One instance is in the <code><a href="/pkg/encoding/json/">encoding/json</a></code>
package, which defines a <code><a href="/pkg/encoding/json/#Marshaler">Marshaler</a></code>
interface. When the JSON encoder receives a value that implements that interface,
the encoder invokes the value's marshaling method to convert it to JSON
instead of doing the standard conversion.
The encoder checks this property at run time with code like:
</p>
<pre>
m, ok := val.(json.Marshaler)
</pre>
<p>
If it's necessary only to ask whether a type implements an interface, without
actually using the interface itself, perhaps as part of an error check, use the blank
identifier to ignore the type-asserted value:
</p>
<pre>
if _, ok := val.(json.Marshaler); ok {
fmt.Printf("value %v of type %T implements json.Marshaler\n", val, val)
}
</pre>
<p>
One place this situation arises is when it is necessary to guarantee within the package implementing the type that
it it actually satisfies the interface.
If a type—for example,
<code><a href="/pkg/encoding/json/#RawMessage">json.RawMessage</a></code>—needs
a custom 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.
If the type inadvertently fails to satisfy the interface, the JSON encoder will still work,
but will not use the custom implementation.
To guarantee that the implementation is correct,
a global declaration using the blank identifier can be used in the package:
</p>
<pre>
var _ json.Marshaler = (*RawMessage)(nil)
</pre>
<p>
In this declaration, the assignment involving a conversion of a
<code>*RawMessage</code> to a <code>Marshaler</code>
requires that <code>*RawMessage</code> implements <code>Marshaler</code>,
and that property will be checked at compile time.
Should the <code>json.Marshaler</code> interface change, this package
will no longer compile and we will be on notice that it needs to be updated.
</p>
<p>
The appearance of the blank identifier in this construct indicates that
the declaration exists only for the type checking,
not to create a variable.
Don't do this for every type that satisfies an interface, though.
By convention, such declarations are only used
when there are no static conversions already present in the code,
which is a rare event.
</p>
<h2 id="embedding">Embedding</h2>
<p>
@ -3146,155 +3338,6 @@ 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/` `$`}}
<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 = (*RawMessage)(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>

18
doc/progs/eff_unused1.go Normal file
View File

@ -0,0 +1,18 @@
// skip
package main
import (
"fmt"
"io"
"log"
"os"
)
func main() {
fd, err := os.Open("test.go")
if err != nil {
log.Fatal(err)
}
// TODO: use fd.
}

22
doc/progs/eff_unused2.go Normal file
View File

@ -0,0 +1,22 @@
// compile
package main
import (
"fmt"
"io"
"log"
"os"
)
var _ = fmt.Printf // For debugging; delete when done.
var _ io.Reader // For debugging; delete when done.
func main() {
fd, err := os.Open("test.go")
if err != nil {
log.Fatal(err)
}
// TODO: use fd.
_ = fd
}

View File

@ -16,6 +16,7 @@ effective_go="
eff_bytesize
eff_qr
eff_sequence
eff_unused2
"
error_handling="

View File

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

View File

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