Explain Type Assertions in Go - go

I'm reading about type assertions x.(T) in The Go Programming Language and don't understand them.
I understand that there are different scenarios:
T is a concrete type or an interface
One (asserted value?) or two (ok) values can be returned
This is what I don't understand:
Why would I use them?
What exactly do they return?
I have also googled on the topic and still don't understand.

Short answer
In one line:
x.(T) asserts that x is not nil and that the value stored in x is of type T.
Why would I use them:
to check x is nil
to check what is the dynamic type held by interface x
to extract the dynamic type from x
What exactly they return:
t := x.(T) => t is of type T; if x is nil, it panics.
t,ok := x.(T) => if x is nil or not of type T => ok is false otherwise ok is true and t is of type T.
Detailed explanation
Imagine you need to calculate area of 4 different shapes: Circle, Square, Rectangle and Triangle. You may define new types with a new method called Area(), like this:
type Circle struct {
Radius float64
}
func (t Circle) Area() float64 {
return math.Pi * t.Radius * t.Radius
}
And for Triangle :
type Triangle struct {
A, B, C float64 // lengths of the sides of a triangle.
}
func (t Triangle) Area() float64 {
p := (t.A + t.B + t.C) / 2.0 // perimeter half
return math.Sqrt(p * (p - t.A) * (p - t.B) * (p - t.C))
}
And for Rectangle :
type Rectangle struct {
A, B float64
}
func (t Rectangle) Area() float64 {
return t.A * t.B
}
And for Square:
type Square struct {
A float64
}
func (t Square) Area() float64 {
return t.A * t.A
}
Here you have Circle, with radius of 1.0, and other shapes with their sides:
shapes := []Shape{
Circle{1.0},
Square{1.772453},
Rectangle{5, 10},
Triangle{10, 4, 7},
}
Interesting! How can we collect them all in one place?
First you need Shape interface to collect them all in one slice of shape []Shape :
type Shape interface {
Area() float64
}
Now you can collect them like this:
shapes := []Shape{
Circle{1.0},
Square{1.772453},
Rectangle{5, 10},
Triangle{10, 4, 7},
}
After all, Circle is a Shape and Triangle is a Shape too.
Now you can print the area of each shape using the single statement v.Area():
for _, v := range shapes {
fmt.Println(v, "\tArea:", v.Area())
}
So Area() is a common interface between all shapes.
Now, how can we calculate and call uncommon method like angles of triangle using above shapes?
func (t Triangle) Angles() []float64 {
return []float64{angle(t.B, t.C, t.A), angle(t.A, t.C, t.B), angle(t.A, t.B, t.C)}
}
func angle(a, b, c float64) float64 {
return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi
}
Now it's time to extract Triangle from above shapes:
for _, v := range shapes {
fmt.Println(v, "\tArea:", v.Area())
if t, ok := v.(Triangle); ok {
fmt.Println("Angles:", t.Angles())
}
}
Using t, ok := v.(Triangle) we requested type assertions, meaning we asked the compiler to try to convert v of type Shape to type Triangle, so that if it's successful, the ok will be true otherwise false, and then if it is successful call t.Angles() to calculate the triangle's three angles.
This is the output:
Circle (Radius: 1) Area: 3.141592653589793
Square (Sides: 1.772453) Area: 3.1415896372090004
Rectangle (Sides: 5, 10) Area: 50
Triangle (Sides: 10, 4, 7) Area: 10.928746497197197
Angles: [128.68218745348943 18.194872338766785 33.12294020774379]
And the whole working sample code:
package main
import "fmt"
import "math"
func main() {
shapes := []Shape{
Circle{1.0},
Square{1.772453},
Rectangle{5, 10},
Triangle{10, 4, 7},
}
for _, v := range shapes {
fmt.Println(v, "\tArea:", v.Area())
if t, ok := v.(Triangle); ok {
fmt.Println("Angles:", t.Angles())
}
}
}
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
type Triangle struct {
A, B, C float64 // lengths of the sides of a triangle.
}
type Rectangle struct {
A, B float64
}
type Square struct {
A float64
}
func (t Circle) Area() float64 {
return math.Pi * t.Radius * t.Radius
}
// Heron's Formula for the area of a triangle
func (t Triangle) Area() float64 {
p := (t.A + t.B + t.C) / 2.0 // perimeter half
return math.Sqrt(p * (p - t.A) * (p - t.B) * (p - t.C))
}
func (t Rectangle) Area() float64 {
return t.A * t.B
}
func (t Square) Area() float64 {
return t.A * t.A
}
func (t Circle) String() string {
return fmt.Sprint("Circle (Radius: ", t.Radius, ")")
}
func (t Triangle) String() string {
return fmt.Sprint("Triangle (Sides: ", t.A, ", ", t.B, ", ", t.C, ")")
}
func (t Rectangle) String() string {
return fmt.Sprint("Rectangle (Sides: ", t.A, ", ", t.B, ")")
}
func (t Square) String() string {
return fmt.Sprint("Square (Sides: ", t.A, ")")
}
func (t Triangle) Angles() []float64 {
return []float64{angle(t.B, t.C, t.A), angle(t.A, t.C, t.B), angle(t.A, t.B, t.C)}
}
func angle(a, b, c float64) float64 {
return math.Acos((a*a+b*b-c*c)/(2*a*b)) * 180.0 / math.Pi
}
Also see:
Type assertions
For an expression x of interface type and a type T, the primary
expression
x.(T)
asserts that x is not nil and that the value stored in x is of type T. The notation x.(T) is called a type assertion.
More precisely, if T is not an interface type, x.(T) asserts that the
dynamic type of x is identical to the type T. In this case, T must
implement the (interface) type of x; otherwise the type assertion is
invalid since it is not possible for x to store a value of type T. If
T is an interface type, x.(T) asserts that the dynamic type of x
implements the interface T.
If the type assertion holds, the value of the expression is the value
stored in x and its type is T. If the type assertion is false, a
run-time panic occurs. In other words, even though the dynamic type of
x is known only at run time, the type of x.(T) is known to be T in a
correct program.
var x interface{} = 7 // x has dynamic type int and value 7
i := x.(int) // i has type int and value 7
type I interface { m() }
var y I
s := y.(string) // illegal: string does not implement I (missing method m)
r := y.(io.Reader) // r has type io.Reader and y must implement both I and io.Reader
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)
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.
EDIT
Question: What does the assertion x.(T) return when T is an interface{} and not a concrete type?
Answer:
It asserts that x is not nil and that the value stored in x is of type T.
E.g. this panics (compile: Success, Run: panic: interface conversion: interface is nil, not interface {}):
package main
func main() {
var i interface{} // nil
var _ = i.(interface{})
}
And this works (Run: OK):
package main
import "fmt"
func main() {
var i interface{} // nil
b, ok := i.(interface{})
fmt.Println(b, ok) // <nil> false
i = 2
c, ok := i.(interface{})
fmt.Println(c, ok) // 2 true
//var j int = c // cannot use c (type interface {}) as type int in assignment: need type assertion
//fmt.Println(j)
}
Output:
<nil> false
2 true
NOTE: here c is of type interface {} and not int.
See this working sample code with commented outputs:
package main
import "fmt"
func main() {
const fm = "'%T'\t'%#[1]v'\t'%[1]v'\t%v\n"
var i interface{}
b, ok := i.(interface{})
fmt.Printf(fm, b, ok) // '<nil>' '<nil>' '<nil>' false
i = 2
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // 'int' '2' '2' true
i = "Hi"
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // 'string' '"Hi"' 'Hi' true
i = new(interface{})
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // '*interface {}' '(*interface {})(0xc042004330)' '0xc042004330' true
i = struct{}{}
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // 'struct {}' 'struct {}{}' '{}' true
i = fmt.Println
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // 'func(...interface {}) (int, error)' '(func(...interface {}) (int, error))(0x456740)' '0x456740' true
i = Shape.Area
b, ok = i.(interface{})
fmt.Printf(fm, b, ok) // 'func(main.Shape) float64' '(func(main.Shape) float64)(0x401910)' '0x401910' true
}
type Shape interface {
Area() float64
}

Common usecase: check if returned error is of a type T.
https://golang.org/ref/spec#Type_assertions
For a single return value assertion: when it fails the program panics.
For a two return values assertion: when it fails second argument is set to false and the program doesn't panic.

A type assertion is the x.(T) notation where x is of interface type and T is a type. Additionally, the actual value stored in x is of type T, and T must satisfy the interface type of x.

Related

What's the difference between a generic slice argument and an argument constrained to slice types?

Consider the experimental package slices. The package is experimental, so I understand the signatures may change; I'm using it to illustrate the issue.
Consider the signatures of two functions from this package, slices.Contains and slices.Grow:
func Contains[E comparable](s []E, v E) bool
func Grow[S ~[]E, E any](s S, n int) S
The first argument to Contains has type []E (slice of Es) with E constrained by comparable (types that are comparable).
The first argument to Grow instead has type S (just S), with S constrained by ~[]E (types whose underlying type is a slice of E)
However it looks like there isn't any practical difference between what operations are allowed inside functions with such type params. If we declare some fake funcs with the same type parameters, we can see that both compile just fine:
As expected, in both functions we can len/cap, append, range, allocate with make, and index with [ ].
func fakeContains[E comparable](s []E, v E) {
fmt.Println(len(s), cap(s))
var e E
fmt.Println(append(s, e))
fmt.Println(make([]E, 4))
for _, x := range s {
fmt.Println(x)
}
fmt.Println(s[0])
fmt.Println(reflect.TypeOf(s).Kind())
}
func fakeGrow[S ~[]E, E any](s S, n int) {
fmt.Println(len(s), cap(s))
var e E
fmt.Println(append(s, e))
fmt.Println(make(S, 4))
for _, x := range s {
fmt.Println(x)
}
fmt.Println(s[0])
fmt.Println(reflect.TypeOf(s).Kind())
}
Even reflect.TypeOf(s).Kind() gives reflect.Slice in all cases.
The functions can also be tested with different types, and all compile:
// compiles just fine
func main() {
type MyUint64 uint64
type MyUint64Slice []uint64
foo := []uint64{0, 1, 2}
fakeContains(foo, 0)
fakeGrow(foo, 5)
bar := []MyUint64{3, 4, 5}
fakeContains(bar, 0)
fakeGrow(bar, 5)
baz := MyUint64Slice{6, 7, 8}
fakeContains(baz, 0)
fakeGrow(baz, 5)
}
The only actual difference in my understanding is that in slices.Grow the argument s S is not a slice. It's just constrained to slice types. And as a matter of fact reflect.TypeOf(s) gives a different output when the arg is an instance of type MyUint64Slice []uint64:
Contains with arg s []E gives reflect.TypeOf(s) -> []uint64
Grow with arg s S gives reflect.TypeOf(s) -> main.MyUint64Slice
However it's not immediately apparent to me what's the practical difference between the two.
Playground with the code: https://gotipplay.golang.org/p/zg2dGtSJwuI
Question
Are these two declarations equivalent in practice? If not, when should I choose one over the other?
It matters if you have to return a slice of the same (possibly named) type as the argument.
If you do not have to return a slice (just some other info e.g. a bool to report if the value is contained), you do not need to use a type parameter that itself constraints to a slice, you may use a type parameter for the element only.
If you have to return a slice of the same type as the input, you must use a type parameter that itself constraints to a slice (e.g. ~[]E).
To demonstrate, let's see these 2 implementations of Grow():
func Grow[S ~[]E, E any](s S, n int) S {
return append(s, make(S, n)...)[:len(s)]
}
func Grow2[E any](s []E, n int) []E {
return append(s, make([]E, n)...)[:len(s)]
}
If you pass a slice of a custom type having a slice as its underlying type, Grow() can return a value of the same type. Grow2() cannot: it can only return a value of an unnamed slice type: []E.
And the demonstration:
x := []int{1}
x2 := Grow(x, 10)
fmt.Printf("x2 %T len=%d cap=%d\n", x2, len(x2), cap(x2))
x3 := Grow2(x, 10)
fmt.Printf("x3 %T len=%d cap=%d\n", x3, len(x3), cap(x3))
type ints []int
y := ints{1}
y2 := Grow(y, 10)
fmt.Printf("y2 %T len=%d cap=%d\n", y2, len(y2), cap(y2))
y3 := Grow2(y, 10)
fmt.Printf("y3 %T len=%d cap=%d\n", y3, len(y3), cap(y3))
Output (try it on the Go Playground):
x2 []int len=1 cap=12
x3 []int len=1 cap=12
y2 main.ints len=1 cap=12
y3 []int len=1 cap=12
As you can see Grow2(y, 10) receives a value of type main.ints and yet it returns a value of type []int. This is not what we would want from it.

How to define interface for methods on struct pointer [duplicate]

This question already has answers here:
X does not implement Y (... method has a pointer receiver)
(4 answers)
Closed 1 year ago.
For structs, it is possible to define a func that can update struct variables. Is there any way to use those functions in interface?
In the following code, I tried to create a minimal example to describe my question. two struct of Rect and Circle are defined. Both of the structs have Perimeter and Expand functions.
Perimeter function calculates the perimeter of the shape. Expand function increases the perimeter of the shape by changing its properties. Also Shape interface with the Perimeter method signature is defined.
viewShapeData function accept input parameter of Shape type. This function view data of the shapes and also run the Perimeter method and view the perimeter of shapes.
package main
import "fmt"
type Shape interface {
Perimeter() float64
}
type Rect struct {
width float64
height float64
}
type Circle struct {
radius float64
}
func (s Rect) Perimeter() float64 {
return 2 * (s.width + s.height)
}
func (s Circle) Perimeter() float64 {
return 2 * 3.14 * s.radius
}
func (s *Rect) Expand(increaseValue float64) {
s.width += increaseValue / 2
}
func (s *Circle) Expand(increaseValue float64) {
s.radius += increaseValue / 3.14 / 2
}
func main() {
a := Circle{radius: 10.0}
b := Rect{width: 2.4, height: 5}
a.Expand(1)
viewShapeData(a)
b.Expand(1)
viewShapeData(b)
}
func viewShapeData(s Shape) {
fmt.Printf("value: %v, Type: %T, Perimeter: %f\n", s, s, s.Perimeter())
}
Now I'm looking for a way to call the Expand method in the viewShapeData function. I tried different ways and applied the following changes in the described code:
type Shape interface {
Perimeter() float64
Expand(float64)
}
func main() {
a := Circle{radius: 10.0}
b := Rect{width: 2.4, height: 5}
viewShapeData(a)
viewShapeData(b)
}
func viewShapeData(s Shape) {
s.Expand(1)
fmt.Printf("value: %v, Type: %T, Perimeter: %f\n", s, s, s.Perimeter())
}
But these errors appear:
cannot use a (type Circle) as type Shape in argument to viewShapeData:
Circle does not implement Shape (Expand method has pointer receiver)
cannot use b (type Rect) as type Shape in argument to viewShapeData:
Rect does not implement Shape (Expand method has pointer receiver)
Please give me a solution or tell me why Golang does not support this kind of coding.
So, if you want to use a pointer receiver, you also should pass a pointer into the function that takes the interface.
For example
func main() {
a := Circle{radius: 10.0}
b := Rect{width: 2.4, height: 5}
a.Expand(1)
viewShapeData(&a)
b.Expand(1)
viewShapeData(&b)
}
https://play.golang.com/p/pmHd5yl_v8p

Conversion between slices with similar element type

I have studied this post, but I cannot figure out how to use it in my case.
1st package
I have a 1st package with this type:
type Vertex struct {
X, Y, Z float32
}
There is a slice like:
var V []Vertex
2nd package
There is a 2nd package whose API cannot be modified. In 2nd package, there is this function:
func Compute(points []struct{X, Y, Z float32}) (err error) {
// ...
return
}
Call 2nd package by 1st package
Inside 1st package I intend to call the 2nd package. Without a loop that will copy all the fields from the source to the target:
err = secondpackage.Compute(V)
But I receive this error:
Cannot use 'V' (type []Vertex) as type []struct {...}
Didn't work
Inspired by this post, I tried to convert by:
points := []struct {
X, Y, Z float32
}(V)
err = secondpackage.Compute(points)
But I'm receiving this error:
Cannot convert expression of type '[]Vertex' to type '[]struct { X, Y, Z float32 }'
Notice here that you are attempting to create a slice:
points := []struct {
X, Y, Z float32
}(V)
But you are not really giving the values the slice should be filled in. For that you should use {V} instead of (V).
Working example:
https://play.golang.org/p/yYaQM-mPPjB
package main
import (
"fmt"
)
type Vertex struct {
X, Y, Z float32
}
var V []Vertex
func Compute(points []struct{ X, Y, Z float32 }) (err error) {
fmt.Printf("%v\n", points)
return nil
}
func main() {
v := Vertex{1, 2, 3}
points := []struct {
X, Y, Z float32
}{v}
Compute(points)
}
Alternatively, you can be more explicit:
points := []struct {
X, Y, Z float32
}{{v.X, v.Y, v.Z}}

Interfaces vs struct methods

I have code with interface:
package main
import (
"math"
"fmt"
)
type Circle struct {
x, y, r float64
}
type Rectangle struct {
x1, y1, x2, y2 float64
}
type Figure interface {
Area() float64
}
func (c *Circle) Area() float64 {
return math.Pi * c.r * c.r
}
func (r *Rectangle) Area() float64 {
return math.Abs(r.x2 - r.x1) * math.Abs(r.y2 - r.y1)
}
func main() {
figures := make([]Figure, 0)
figures = append(figures, &Circle{0, 0, 10})
figures = append(figures, &Rectangle{0, 0, 10, 20})
for _, figure := range figures {
fmt.Print(figure.Area(), "\n")
}
}
output:
314.159265
200
and code with only methods for my struct:
package main
import (
"math"
"fmt"
)
type Circle struct {
x, y, r float64
}
type Rectangle struct {
x1, y1, x2, y2 float64
}
func (c *Circle) Area() float64 {
return math.Pi * c.r * c.r
}
func (r *Rectangle) Area() float64 {
return math.Abs(r.x2 - r.x1) * math.Abs(r.y2 - r.y1)
}
func main() {
c := Circle{0,0,10}
r := Rectangle{0,0,10,20}
fmt.Print(c.Area(), "\n")
fmt.Print(r.Area(), "\n")
}
and the same output:
314.1592653589793
200
When I use interface I have extra code in the form of interfaces declaration. If the interfaces perfectly implement polymorphism in Go, why then the methods of structures? What plus from interfaces, where is the difference? May be my example not good. Thank you!
You should see it in your own code: in the first case you could handle all in unity, as values of Figure and so you could store them in a slice (of type []Figure), and range over them and call their Area() method.
In the 2nd case without interfaces you did not store them in a slice and you did not use a loop, instead you had to call Area() manually on each instance.
Without interfaces there is no type for which to create a slice of and store each in it. The only option would be the interface{} type:
figures := make([]interface{}, 0)
figures = append(figures, &Circle{0, 0, 10})
figures = append(figures, &Rectangle{0, 0, 10, 20})
But then when ranging over them, you wouldn't be able to call their Area() method, as the interface{} type does not define any methods.
for _, figure := range figures {
fmt.Print(figure.Area(), "\n") // COMPILE-TIME error
}
If you only have 2 instances, you can call their Area() methods manually, it would be even shorter. But if you have a hundred or a thousand...
Without repeating advantages of interfaces, see possible duplicate:
Why are interfaces needed in Golang?

golang, create a variable from another one

I want to :
Create a variable with the type of another one. The source variable is numeric (int, int16, float32, ...)
Make some simple operations (+, -, ...) on this variable.
This code works fine :
package main
import (
"fmt"
"reflect"
)
func init1(v interface{}) interface{} {
switch reflect.ValueOf(v).Kind() {
case reflect.Int:
return int(0)
case reflect.Float32:
return float32(0)
case reflect.Float64:
return float64(0)
}
return nil
}
func sum(x, y interface{}) interface{} {
switch reflect.ValueOf(x).Kind() {
case reflect.Int:
return x.(int) + y.(int)
case reflect.Float32:
return x.(float32) + y.(float32)
case reflect.Float64:
return x.(float64) + y.(float64)
}
return nil
}
func main() {
v0 := 222.33
x0 := init1(v0)
x0 = sum(x0, v0)
x0 = sum(x0, v0)
fmt.Printf("v=%v, x=%v type x=%T\n", v0, x0, x0)
v1 := 33
x1 := init1(v1)
x1 = sum(x1, v1)
x1 = sum(x1, v1)
fmt.Printf("v=%v, x=%v type x=%T\n", v1, x1, x1)
}
Result :
v=222.33, x=444.66 type x=float64
v=33, x=66 type x=int
Is there a more elegant solution (without the two switch blocks) to do the same job ?
Thanks for your help.
Is there a more elegant solution (without the two switch blocks) to do the same job ?
No there is not.
You could use reflection but would still need a switch for int, uints and floats.
(Btw: Don't do such things.)
There is more elegant and more effective solution - do not use interface{} as casting to/from interface{} will result in spending resources (CPU/memory) on boxing/unboxing.
// Bad solution - 11 lines of slow code
func sumBad(x, y interface{}) interface{} {
switch reflect.ValueOf(x).Kind() {
case reflect.Int:
return x.(int) + y.(int)
case reflect.Float32:
return x.(float32) + y.(float32)
case reflect.Float64:
return x.(float64) + y.(float64)
}
return nil
}
// Good solution - 9 lines of code that works faster
func sumInt(a, b int) int {
return a + b
}
func sumFloat32(a, b float32) float32 {
return a + b
}
func sumFloat64(a, b float64) float64 {
return a + b
}
If you have much more code inside the function you can use Go code generation to generate the functions for as many types as you want.
Another alternative would be to choose a type that cover all your needs. For example can you use just float64 and cast parameters to it whenever needed?

Resources