1
0
mirror of https://github.com/golang/go synced 2024-11-22 03:04:41 -07:00

effective_go.html: add a section on type assertions

The information was missing, oddly enough.

R=golang-dev, rsc, iant
CC=golang-dev
https://golang.org/cl/7636044
This commit is contained in:
Rob Pike 2013-03-08 13:53:17 -08:00
parent 9e329a0d16
commit 33e8ca4d67

View File

@ -2092,6 +2092,91 @@ and <code>[]int</code>), each of which does some part of the job.
That's more unusual in practice but can be effective.
</p>
<h3 id="interface_conversions">Interface conversions and type assertions</h3>
<p>
<a href="#type_switch">Type switches</a> are a form of conversion: they take an interface and, for
each case in the switch, in a sense convert it to the type of that case.
Here's a simplified version of how the code under <code>fmt.Printf</code> turns a value into
a string using a type switch.
If it's already a string, we want the actual string value held by the interface, while if it has a
<code>String</code> method we want the result of calling the method.
</p>
<pre>
type Stringer interface {
String() string
}
var value interface{} // Value provided by caller.
switch str := value.(type) {
case string:
return str
case Stringer:
return str.String()
}
</pre>
<p>
The first case finds a concrete value; the second converts the interface into another interface.
It's perfectly fine to mix types this way.
</p>
<p>
What if there's only one type we care about? If we know the value holds a <code>string</code>
and we just want to extract it?
A one-case type switch would do, but so would a <em>type assertion</em>.
A type assertion takes an interface value and extracts from it a value of the specified explicit type.
The syntax borrows from the clause opening a type switch, but with an explicit
type rather than the <code>type</code> keyword:
<pre>
value.(typeName)
</pre>
<p>
and the result is a new value with the static type <code>typeName</code>.
That type must either be the concrete type held by the interface, or a second interface
type that the value can be converted to.
To extract the string we know is in the value, we could write:
</p>
<pre>
str := value.(string)
</pre>
<p>
But if it turns out that the value does not contain a string, the program will crash with a run-time error.
To guard against that, use the "comma, ok" idiom to test, safely, whether the value is a string:
</p>
<pre>
str, ok := value.(string)
if ok {
fmt.Printf("string value is: %q\n", str)
} else {
fmt.Printf("value is not a string\n")
}
</pre>
<p>
If the type assertion fails, <code>str</code> will still exist and be of type string, but it will have
the zero value, an empty string.
</p>
<p>
As an illustration of the capability, here's an <code>if</code>-<code>else</code>
statement that's equivalent to the type switch that opened this section.
</p>
<pre>
if str, ok := value.(string); ok {
return str
} else if str, ok := value.(Stringer); ok {
return str.String()
}
</pre>
<h3 id="generality">Generality</h3>
<p>
If a type exists only to implement an interface
@ -2449,7 +2534,7 @@ package, which defines a <code><a href="/pkg/encoding/json/#Marshaler">Marshaler
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:
The encoder checks this property at run time with a <a href="interface_conversions">type assertion</a> like:
</p>
<pre>
@ -3129,11 +3214,8 @@ for try := 0; try &lt; 2; try++ {
</pre>
<p>
The second <code>if</code> statement here is idiomatic Go.
The type assertion <code>err.(*os.PathError)</code> is
checked with the "comma ok" idiom (mentioned <a href="#maps">earlier</a>
in the context of examining maps).
If the type assertion fails, <code>ok</code> will be false, and <code>e</code>
The second <code>if</code> statement here is another <a href="#interface_conversion">type assertion</a>.
If it fails, <code>ok</code> will be false, and <code>e</code>
will be <code>nil</code>.
If it succeeds, <code>ok</code> will be true, which means the
error was of type <code>*os.PathError</code>, and then so is <code>e</code>,