I'm trying to identify a type that implements an interface using a switch:
package main
import "fmt"
type Abser interface {
Abs() float64
}
type Vertex struct {
X, Y int
}
func (v *Vertex) Abs() float64 {
return float64(v.X)
}
func do(i interface{}) {
switch v := i.(type) {
case Abser:
fmt.Println("theres an Abser")
case Vertex:
fmt.Println("theres a Vertex")
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(Vertex{1,2})
}
This code outputs the value for Vertex instead of Abser. Ideally it should have output theres an Abser since Vertex implements the Abs() method. Is it possible to use a switch-case to do this?
It is not about the switch command, but about pointer receivers.
If you change func (v *Vertex) Abs() float64 to func (v Vertex) Abs() float64, it will give the output theres an Abser.
In general code, a type can use its pointer method - you can call Vertex{1,2}.Abs(), but what happened behind it is that the Go compiler rewrites it as (&Vertex{1,2}).Abs() for you. So Vertex does not implement Abser.
But on the contrary, a pointer type has all the methods its underlying type has. So even if the API is defined as func (v Vertex) Abs() float64, &Vertex{1,2} is still an Abser. See: https://play.golang.org/p/ONnsjApMywO
See Also:
Method set: https://golang.org/ref/spec#Method_sets
The problem is that Vertex isn't an Abser, only *Vertex is, if you change your Abs() impl to
func (v Vertex) Abs() float64 {
return float64(v.X)
}
then Vertex itself is an Abser and your code will print theres an Abser
Related
I am testing out generics in go 1.18 and took a look at this example.
I would like to recreate that example but instead be able to pass in a slice of int or slice of float instead, and in the function I'll just sum up everything in the slice.
This is when I ran into some issues just iterating the slice. This is what I tried:
package main
import "fmt"
// NumberSlice constraint
type NumberSlice interface {
[]int64 | []float64
}
func add[N NumberSlice](n N) {
// want: to range over n and print value of v
for _, v := range n {
fmt.Println(v)
}
}
func main() {
ints := []int64{1, 2}
add(ints)
}
I got the error:
cannot range over n (variable of type N constrained by NumberSlice) (N has no core type)
How do I accomplish this?
A core type, for an interface (including an interface constraint) is defined as follows:
An interface T has a core type if one of the following conditions is
satisfied:
There is a single type U which is the underlying type of all types in the type set of T
or the type set of T contains only channel types with identical element type E, and all directional channels have the same direction.
Your interface constraint has no core type, because it has two underlying types: []int64 and []float64.
Therefore you can't use it where a core type is required. Notably range and make.
You can change the interface to require the base types, and then specify the slice in the function signature:
// still no core type...
type Number interface {
int64 | float64
}
// ...but the argument will be instantiated with either int64 or float64
func add[N Number](n []N) {
for _, v := range n {
fmt.Println(v)
}
}
This also works, but it's way more verbose:
type NumberSlice[N int64 | float64] interface {
// one core type []N
~[]N
}
func add[S NumberSlice[N], N int64 | float64](n S) {
for _, v := range n {
fmt.Println(v)
}
}
Could something like this work for you?
package main
import "fmt"
type NumberOrFloat interface {
int64 | float64
}
func add[N NumberOrFloat](n []N) {
for _, v := range n {
fmt.Println(v)
}
}
func main() {
ints := []int64{1, 2}
add(ints)
}
The difference here is that you define type constraints on array elements (not on array types): []N
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
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).
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.
Can anyone explain why the calling of a.Abs() works?
In my opinion, 'a' is a variable of type *Vertex, but the type *Vertex doesn't implement the method Abs.
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
func main() {
var a Abser
v := Vertex{3, 4}
a = &v // a *Vertex implements Abser
// In the following line, v is a *Vertex (not Vertex)
// and does NOT implement Abser, but why does this calling work?
fmt.Println(a.Abs())
}
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
From the fine specification:
Method sets
[...] The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T). [...]
Your Abs function is in the method sets of both Vertex and *Vertex so *Vertex is an Abser just like Vertex is.
Other related sections:
Method values
Selectors
In general, pointers are automatically dereferenced when possible so you can say x.M() and x.V without worrying about whether or not x is a pointer and there is no need for C's -> or manual dereferencing (i.e. (*x).M() or (*x).V).