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

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

Related

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).

Constructing instances of types with pointer and value receivers [duplicate]

This question already has answers here:
X does not implement Y (... method has a pointer receiver)
(4 answers)
Closed 4 years ago.
Lets take this very small example, having a function that modify value inside the struct:
package learn
type Point struct {
x int
y int
}
func (p *Point) Set(x int, y int) {
p.x = x
p.y = y
}
this works properly, used like this for instance:
package main
import (
"NewPattern/learn"
"fmt"
)
func main() {
p := learn.Point{}
p.Set(5, 6)
fmt.Print(p)
}
it outputs the expected value: {5,6}
Now let's say I don't want the user having a constructor, I can change the code by adding this function:
func NewPoint(x int, y int) Point {
return Point{x, y}
}
then I can use in main like this:
func main() {
p := learn.NewPoint(3, 8)
fmt.Print(p)
p.Set(5, 6)
fmt.Print(p)
}
and it works as expected returning {3 8}{5 6}.
Well now we want to prevent creating point without calling the constructor - not really the case here, but can make sense for complex classes - so we avoid exporting Point and we create an interface instead, so I refactored the code like this: (this is not working!)
package learn
type point struct {
x int
y int
}
type Point interface {
Set(x int, y int)
}
func (p *point) Set(x int, y int) {
p.x = x
p.y = y
}
func NewPoint(x int, y int) Point {
return point{x, y} //error here
}
This says:
cannot use point literal (type point) as type Point in return argument:
point does not implement Point (Set method has pointer receiver)
I can "fix" this by modifying the method in:
func NewPoint(x int, y int) point {
return point{x, y}
}
but this just move the error in main, that is refactored like:
func main() {
var p learn.Point
p = learn.NewPoint(3, 8) //error here!
fmt.Print(p)
p.Set(5, 6)
fmt.Print(p)
}
and the error is:
cannot use learn.NewPoint(3, 8) (type learn.point) as type learn.Point in assignment:
learn.point does not implement learn.Point (Set method has pointer receiver)
by googling I managed to solve in this way:
func NewPoint(x int, y int) *point {
return &point{x, y}
}
but as a result in the main we are obtaining: &{3 8}&{5 6} as a print, ad as well I don't get actually what is happening behind the scenes.
I guess this is somehow related by having things passed and maybe "returned" by value, is this the case? But I don't know how the first examples without interface worked without effort. Can please someone clarify these details that I think are essential to Go understanding.
point and *point (i.e the pointer to point) are two different types. In your code the interface Point is implemented by *point type. You can implement the constructor as:
func NewPoint(x int, y int) Point {
return &point{x, y}
}
The printing will show & befor the points value as the underlying value is a pointer.

what is the best way to implement a constructor/init methods in Go

I have a program as below:
package main
//Define declare variables
type Define struct {
len int
breath int
}
//Area calculate area
func (e *Define) Area() (a int) {
a = e.len * e.breath
return a
}
I call the above program in :
package main
func main() {
y := Define{10, 10}
x := y.Area()
print(x)
}
I would like make the function Area() as part of struct initialization. Currently, I have to create a new object for "Define" ie "y" and then call the method Area. Instead is there a way that Area methods auto calculates once I create the object?
I'd rename Define to something better like Geometry. Usually in Golang, New... is used as a "constructor"
Since you said you wanted area to be autocalculated, include the area as a struct field. Here's how I'd go about it (https://play.golang.org/p/4y6UVTTT34Z):
package main
//variables
type Geometry struct {
len int
breath int
area int
}
// Constructor
func NewGeometry(len int, breadth int) *Geometry {
g := &Geometry{len, breadth, Area(len, breadth)}
return g
}
//Area calculate area
func Area(len, breadth int) (a int) {
return len * breadth
}
func main() {
g := NewGeometry(10, 2)
fmt.Println(g.area)
}
Go has the concept of "Constructors" that may cover your use case. Combined with exporting it allows you to encapsulate initialization by hiding specifics of calculation from callers:
package main
//Define declare variables
type Define struct {
len int
breath int
area int
}
func (e Define) Area() int {
return e.area
}
func NewDefine(l, b int) Define {
d := Define{
len: l,
breath: b,
area: calculateArea(l, b),
}
return d
}
The pattern to focus on is the exported NewX. It is extremely common to see constructors named NewX which will initialize and return a struct. The above delegates to an un-exported calculateArea function. Of course there are many different possible ways for you to structure your program. calculateArea still encapsulates the area calculation for trivial unit testing, while hiding it from callers by not exporting it.

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?

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