Interfaces vs struct methods - go

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?

Related

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}}

Golang interface of different types - confusion when passing by value

playground
package main
import (
"fmt"
"math"
)
// types
type square struct {
length float64
}
type circle struct {
radius float64
}
type shape interface {
area() float64
}
// methods
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (s square) area() float64 {
return s.length * s.length
}
func main() {
c := circle{
radius: 2.5,
}
s := square{
length: 25.5,
}
info(c)
info(s)
}
func info(s shape) {
x := s.area()
fmt.Println(x)
}
this all works as expected, however, I don't fully understand why. I understand what is going on in terms of creating the interface with the structs, however, what confuses me is how we under func main() after creating type literals of circle and square when I pass the arguments info(c) and info(s) how Go understands which method we are calling with the right argument. I feel like there may need to be type assertion involved of some sort with:
func info(s shape) {
if s.(type) == circle {
c.area()
}
}
Go knows that type shape is going to be either circle or square and then in the background is Go doing that type selection for us? I hope my question was clear enough, thank you.
Go knows that type shape is going to be either circle or square
No. Go knows that both types implement the shape interface. This makes it possible to use instances of circle and square as arguments for func info(s shape).

Unexpected int at end of statement

I'm struggling a bit with this piece of Go code. I have been searching all over the place, but can't understand what is wrong about it.
Error message is: syntax error: unexpected int at end of statement
for that line near the bottom: func (TOHLCV TOHLCVs) Len() int {
I also have this error message for the second to the last line of code:
syntax error: non-declaration statement outside function body
In case the 2 errors are related
package main
import (
"fmt"
"time"
"strconv"
//from https://github.com/pplcc/plotext/
"log"
"os"
"github.com/360EntSecGroup-Skylar/excelize"
"github.com/pplcc/plotext/custplotter"
"gonum.org/v1/plot"
"github.com/pplcc/plotext"
"gonum.org/v1/plot/vg/vgimg"
"gonum.org/v1/plot/vg/draw"
)
// Len implements the Len method of the TOHLCVer interface.
func (TOHLCV TOHLCVs) Len() int {
return len(TOHLCV)
func main() {
//read excel file******************************************
xlsx, err := excelize.OpenFile("/media/Snaps/test snaps.xlsm")
if err != nil {
fmt.Println(err)
return
}
//read all rows into df
df := xlsx.GetRows("ticker_2")
type TOHLCVer interface {
// Len returns the number of time, open, high, low, close, volume tuples.
Len() int
// TOHLCV returns an time, open, high, low, close, volume tuple.
TOHLCV(int) (float64, float64, float64, float64, float64, float64)
}
type TOHLCVs []struct{ T, O, H, L, C, V float64 }
// Len implements the Len method of the TOHLCVer interface.
func (TOHLCV TOHLCVs) Len() int {
return len(TOHLCV)
}
df3 := make(TOHLCVs, 60) // create slice for 60 rows
idx := 0
this code is adapted from:
https://github.com/pplcc/plotext/blob/master/custplotter/tohlcv.go
Function declarations need to be moved out of other functions, Like this:
package main
import (
"fmt"
"github.com/360EntSecGroup-Skylar/excelize"
)
type TOHLCVer interface {
// Len returns the number of time, open, high, low, close, volume tuples.
Len() int
// TOHLCV returns an time, open, high, low, close, volume tuple.
TOHLCV(int) (float64, float64, float64, float64, float64, float64)
}
type TOHLCVs []struct{ T, O, H, L, C, V float64 }
// Len implements the Len method of the TOHLCVer interface.
func (TOHLCV TOHLCVs) Len() int {
return len(TOHLCV)
}
func main() {
//read excel file******************************************
xlsx, err := excelize.OpenFile("/media/Snaps/test snaps.xlsm")
if err != nil {
fmt.Println(err)
return
}
//read all rows into df
df := xlsx.GetRows("ticker_2")
df3 := make(TOHLCVs, 60) // create slice for 60 rows
idx := 0
}
Type declarations can be inside of a function. But, in this case, it makes more sense for them to be outside. There are some situations where it's helpful to declare a function inside another function:
Passing a function as an argument: https://play.golang.org/p/4NgeUvsexto
Assigning an anonymous function to a variable: https://play.golang.org/p/r1DF9_iP0-k
(I'm not sure about the exact logic you're looking for - the above code doesn't do anything yet. I'll also caution against creating an interface unless you needed it.)
So based on answer of #Tyler Bui-Palsulich and #aec my code now looks like below, and no more error messages :-), thanks all !
package main
import (
"fmt"
"time"
"strconv"
//from https://github.com/pplcc/plotext/
"log"
"os"
"github.com/360EntSecGroup-Skylar/excelize"
"github.com/pplcc/plotext/custplotter"
//"github.com/pplcc/plotext/examples"
"gonum.org/v1/plot"
"github.com/pplcc/plotext"
"gonum.org/v1/plot/vg/vgimg"
"gonum.org/v1/plot/vg/draw"
)
// Len implements the Len method of the TOHLCVer interface.
//func (TOHLCV TOHLCVs) Len() int {
// return len(TOHLCV)
//}
type TOHLCVer interface {
// Len returns the number of time, open, high, low, close, volume tuples.
Len() int
// TOHLCV returns an time, open, high, low, close, volume tuple.
TOHLCV(int) (float64, float64, float64, float64, float64, float64)
}
type TOHLCVs []struct{ T, O, H, L, C, V float64 }
// Len implements the Len method of the TOHLCVer interface.
func (TOHLCV TOHLCVs) Len() int {
return len(TOHLCV)
}
// TOHLCV implements the TOHLCV method of the TOHLCVer interface.
func (TOHLCV TOHLCVs) TOHLCV(i int) (float64, float64, float64, float64, float64, float64) {
return TOHLCV[i].T, TOHLCV[i].O, TOHLCV[i].H, TOHLCV[i].L, TOHLCV[i].C, TOHLCV[i].V
}
func main() {
start := time.Now()
//create data for each chart****************************************************
//******************************************************************************
//read excel file******************************************
xlsx, err := excelize.OpenFile("/media/hugues/M.2 windows/Hugues/Snaps/test snaps.xlsm")
if err != nil {
fmt.Println(err)
return
}
//read all rows into df
df := xlsx.GetRows("ticker_2")
df3 := make(TOHLCVs, 60) // create slice for 60 rows
idx := 0
for _, row := range df[1:61] { // read 60 rows
df3[idx].T, err = strconv.ParseFloat(row[28], 64)
df3[idx].O, err = strconv.ParseFloat(row[29], 64)
df3[idx].H, err = strconv.ParseFloat(row[30], 64)
df3[idx].L, err = strconv.ParseFloat(row[31], 64)
df3[idx].C, err = strconv.ParseFloat(row[32], 64)
df3[idx].V, err = strconv.ParseFloat(row[33], 64)
idx++
}

Explain Type Assertions in 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.

Resources