This article was written for 12/24 of Go Quiz Advent Calender 2020.
Yesterday was @ binzume's "Go quiz: int = float64".
I wanted to ask a question about Index expressions, but I felt it was similar to Question on Twitter before, so I hurriedly replaced it with a question about Type assertions.
Now, let's get down to the main subject.
What will the output look like when I run the following code? Playground
package main
import (
"fmt"
)
type I interface {
Hello()
}
type T struct{}
func (T) Hello() {
fmt.Println("Hello!")
}
func main() {
var i I = nil
v, _ := i.(T)
v.Hello()
}
TL;DR --Assertions to type T used for special forms of assignment or initialization generate an additional untyped bool value. --If no type assertion is preserved, the first return value will be a zero value of type T and the second return value will be false for untyped bool. --In this case, ** run-time panic does not occur **
First, let's look at Type assersions (https://golang.org/ref/spec#Type_assertions).
For an expression
x
of interface type and a typeT
, the primary expressionx.(T)
asserts that x is not nil and that the value stored inx
is of typeT
. The notation x.(T) is called a type assertion.
The interface type expressions x
and type T
are written as follows.
--The notation x. (T)
is called a type assertion.
--x. (T)
guarantees that x
is not nil and the value stored in x
is of type T
This means that when a type assertion of x. (T)
is performed, it is guaranteed that x
is not nil. However, in this question, i in i. (T)
is nil. What does this mean? The answer can be found in the text below.
A type assertion used in an assignment or initialization of the special form
v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok T1 = x.(T)
yields an additional untyped boolean value. The value of ok is true if the assertion holds. Otherwise it is false and the value of v is the zero value for type T. No run-time panic occurs in this case.
--The type assertions used for these special forms of assignment or initialization generate additional untyped Boolean values.
--The value of ok
is true
if the assertion is retained
--Otherwise it is false
and the value of v is a zero value of type T
.
--In this case, ** run-time panic does not occur **
That is, when assigning and initializing in this format, run-time panic does not occur and a variable of type T
is stored in the first return value.
Here, I think that "this format" refers to the format that accepts up to the second return value. This is because if you don't receive the second form, you'll end up with the code below, which is a run-time panic.
package main
import (
"fmt"
)
type I interface {
Hello()
}
type T struct{}
func (T) Hello() {
fmt.Println("Hello!")
}
func main() {
var i I = nil
var v = i.(T) // panic: interface conversion: main.I is nil, not main.T
v.Hello()
}
~~ When I saw this, the definition "x. (T)
guarantees that x
is not nil "was written at the beginning of Type assertions. I felt it was inconsistent with. ~~
2020/12/24 postscript: If you think about it, the second return value contains false and doesn't hold any type assertions. Therefore, the definition written at the beginning does not apply, so there was no problem.
Anyway, in this case the information of type T
is retained, so of course you can also executev.Hello ()
.
As a result, Hello!
Is output.
As a countermeasure, if you receive the second return value, you should insert a branch as shown below.
package main
import (
"fmt"
"log"
"os"
)
type I interface {
Hello()
}
type T struct{}
func (T) Hello() {
fmt.Println("Hello!")
}
func main() {
var i I = nil
v, ok := i.(T)
if !ok {
log.Println("invalid assertion")
os.Exit(1)
}
v.Hello()
}
2021/01/14 postscript:
This format can also be used when converting from nil
to""
(empty string). It can be used when there is a record containing Null.
package main
import (
"fmt"
)
func main() {
var n interface{} = nil
if _, ok := n.(string); !ok {
n = ""
}
fmt.Println("n: ", n.(string))
fmt.Println("n==nil: ", n == nil)
}
Thank you for reading. Tomorrow, there seems to be @ tenntenn's last boss problem. I'm looking forward to it.