A beginner question: About Interfaces

Hi All,
In this script, I started to apply interface in the structs.

type Circle struct {
	x float64
	y float64
	r float64
}

type Rectangle struct {
	x1, y1, x2, y2 float64

}

type Shape interface {
	area() float64
}

type MultiShape struct {
	shapes []Shape
}

func distance(x1, y1, x2, y2 float64) float64 {
	a := x2 - x1
	b := y2 - y1
	return math.Sqrt(a*a + b*b)
}


func (c *Circle) area() float64 {  \\ when I take out this `*`, this scripts worked 
	return math.Pi * c.r * c.r
}

func (r *Rectangle) area() float64 { \\ when i took out this `*`, this scripts worked
	l := distance(r.x1,r.y1, r.x1, r.y2)
	w := distance(r.x1,r.y1, r.x2, r.y1)
	return l * w
}

func (m *MultiShape) area() float64 {
	var area float64
	for _, s := range m.shapes {
		area += s.area()
	}
	return area
}

func main() {
	multiShape := MultiShape{[]Shape{
		Circle{0,0,5},
		Rectangle{0,0,10,10},
	},}
	fmt.Println(multiShape.area())
}

However, after running this scripts, i got following errors.

./Chapter7.go:93: cannot use Circle literal (type Circle) as type Shape in array or slice literal:
	Circle does not implement Shape (area method has pointer receiver)
./Chapter7.go:94: cannot use Rectangle literal (type Rectangle) as type Shape in array or slice literal:
	Rectangle does not implement Shape (area method has pointer receiver)

If I think this correctly, Circle{0,0,5} or Rectangle{0,0,10,10} because they are in type of []Shape and the methods area() is under type Shape interface, so I thought we don’t need to add & to Circle{0,0,5} or Rectangle{0,0,10,10} to call func with pointer type because it is called with method area().

But in this case, when i took out * from func func (c *Circle) or func (r *Rectangle), it worked without error. Could anyone have any ideas about this? :sweat_smile:

Another way to fix these errors is:

multiShape := MultiShape{[]Shape{
	&Circle{0, 0, 5},
	&Rectangle{0, 0, 10, 10},
}}

The error comes from the fact that an interface has a pointer to the underlying Circle or Rectangle, but needs to behave as though the function was called on whatever the original type was. The errors remove magic from the interface implementation at the same time as they make the code read more transparently.

Could you explain a bit more general? Still a bit confused. :sweat:

whether interface has a pointer or not depends on the underlying Circle or Rectangle, right? For example, in this case, because of func (c *Circle) is pointer type, so interface has a pointer to this underlying *Circle, so if it is just func (c Circle) without pointer, interface cannot take &Circle to call func (c Cirlce) but instead can call func (c *Circle), am i right?

So method area in the interface type, isn’t it suppose to be working like this Circle.area() that we don’t need to worry about changing Circle to &Circle??

Whether an interface has a pointer or not depends on what you assign to the interface.

var a Shape = Circle{}
var b Shape = &Circle{}

a has a Circle, b has a *Circle.

The method sets of Circle and *Circle are different. The method set of Circle contains all methods declared with a receiver of type Circle. The method set of *Circle contains all methods declared with receivers of type Circle and *Circle.

If x is addressable and &x’s method set contains m, x.m() is shorthand for (&x).m() - Calls

Interfaces aren’t addressable, so this short hand does not apply.

Since the method set of Circle (in your original example) does not have a method area() float64, Circle does not implement Shape.

The method set of *Circle has a method area() float64, so *Circle implements Shape.

2 Likes

Thanks mate, I think I get your point.

I feel like , in the go programming language, all things work based on type matching.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.