From 33e8ca4d67ecd1fb02d9189160cb3a91b4285748 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Fri, 8 Mar 2013 13:53:17 -0800 Subject: [PATCH] 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 --- doc/effective_go.html | 94 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/doc/effective_go.html b/doc/effective_go.html index 570ca052347..427a88506c4 100644 --- a/doc/effective_go.html +++ b/doc/effective_go.html @@ -2092,6 +2092,91 @@ and []int), each of which does some part of the job. That's more unusual in practice but can be effective.

+

Interface conversions and type assertions

+ +

+Type switches 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 fmt.Printf 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 +String method we want the result of calling the method. +

+ +
+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()
+}
+
+ +

+The first case finds a concrete value; the second converts the interface into another interface. +It's perfectly fine to mix types this way. +

+ +

+What if there's only one type we care about? If we know the value holds a string +and we just want to extract it? +A one-case type switch would do, but so would a type assertion. +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 type keyword: + +

+value.(typeName)
+
+ +

+and the result is a new value with the static type typeName. +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: +

+ +
+str := value.(string)
+
+ +

+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: +

+ +
+str, ok := value.(string)
+if ok {
+    fmt.Printf("string value is: %q\n", str)
+} else {
+    fmt.Printf("value is not a string\n")
+}
+
+ +

+If the type assertion fails, str will still exist and be of type string, but it will have +the zero value, an empty string. +

+ +

+As an illustration of the capability, here's an if-else +statement that's equivalent to the type switch that opened this section. +

+ +
+if str, ok := value.(string); ok {
+    return str
+} else if str, ok := value.(Stringer); ok {
+    return str.String()
+}
+
+

Generality

If a type exists only to implement an interface @@ -2449,7 +2534,7 @@ package, which defines a 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 type assertion like:

@@ -3129,11 +3214,8 @@ for try := 0; try < 2; try++ {
 

-The second if statement here is idiomatic Go. -The type assertion err.(*os.PathError) is -checked with the "comma ok" idiom (mentioned earlier -in the context of examining maps). -If the type assertion fails, ok will be false, and e +The second if statement here is another type assertion. +If it fails, ok will be false, and e will be nil. If it succeeds, ok will be true, which means the error was of type *os.PathError, and then so is e,