If I use pointer receiver, the following code has exception at a=v since it is defined on pointer v, it makes sense.
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs(x int) float64 //all types needs to implement this interface
}
type Vertex struct {
X float64
}
func (v *Vertex) Abs(x int) float64 {
return math.Abs(float64(x))
}
func main() {
/*define the interface and assign to it*/
var a Abser
v := Vertex{-3}
a = &v
fmt.Println(a.Abs(-3))
a = v
fmt.Println(a.Abs(-3))
}
But if I change the function of Abs to
func (v Vertex) Abs(x int) float64 {
return math.Abs(float64(x))
}
both a=v and a=&v works, what is the reason behind that?
Understand it like this as I do not have right resources to quote in answer; Go is happy to pass a copy of pointer struct as value when interface is implemented on value, you can check this by printing the address of variable; This is due to the fact that this operation is considered safe and cannot mutate the original value;
Related
I have a couple custom types that I need to process in the same way. It seems like a perfect use for generics. In the process, I need to call methods on instances of the types, and those methods return different instances of the same types, and then I need to call methods on those returned instances, which I can't get to work. For the purpose of this question, I've fabricated a much simpler set of types and a process that exemplifies the problem I'm running in to.
Here's a working example without generics that shows the types (Circle and Square), and a process (.Bigger().Smaller()) I'll be trying to abstract into a generic function later (online demo):
package main
import (
"fmt"
)
type Circle struct{ r float64 }
func NewCircle(r float64) *Circle { return &Circle{r: r} }
func (c *Circle) Radius() float64 { return c.r }
func (c *Circle) Bigger() *Circle { return &Circle{r: c.r + 1} }
func (c *Circle) Smaller() *Circle { return &Circle{r: c.r - 1} }
type Square struct{ s float64 }
func NewSquare(s float64) *Square { return &Square{s: s} }
func (s *Square) Side() float64 { return s.s }
func (s1 *Square) Bigger() *Square { return &Square{s: s1.s + 1} }
func (s1 *Square) Smaller() *Square { return &Square{s: s1.s - 1} }
func main() {
fmt.Println(NewCircle(3).Bigger().Smaller().Radius()) // prints 3
fmt.Println(NewSquare(6).Bigger().Smaller().Side()) // prints 6
}
The first thing I do to make a generic function is to define a type constraint:
type ShapeType interface {
*Circle | *Square
}
I'll be passing a ShapeType to a process method, and I need to be able to call methods on the ShapeType instance, so I need to define another type constraint which specifies the methods that can be called on a ShapeType:
type Shape[ST ShapeType] interface {
Bigger() ST
Smaller() ST
}
With these, I can write a process method (online demo):
func process[ST ShapeType](s Shape[ST]) ST {
return s.Bigger().Smaller()
}
This fails to compile however, as the return value of s.Bigger() is an ST, not a Shape[ST], so go doesn't know that it can then call Smaller() on the return value of s.Bigger(). In go's words:
s.Bigger().Smaller undefined (type ST has no field or method Smaller)
If Bigger() and Smaller() didn't return instances of their receiver types, I could write:
type Shape interface {
*Circle | *Square
Bigger()
Smaller()
}
func process[S Shape](x S) S {
x.Bigger().Smaller()
return x // I guess we wouldn't even have to return x, but just for example's sake
}
Instead I would need to write:
type Shape interface {
*Circle | *Square
Bigger() Shape
Smaller() Shape
}
and it appears go doesn't like self-referential type constraints.
If it were possible to assert/convert a concrete type to an interface it conforms to, then I could make it work, but it doesn't appear to be possible to do that (online demo):
func process[ST ShapeType](s Shape[ST]) ST {
s1 := s.Bigger()
s2 := s1.(Shape[ST]) // go is not happy here
return s2.Smaller()
}
For this, go says:
cannot use type assertion on type parameter value s1 (variable of type ST constrained by ShapeType)
I don't know what else to try.
Is it possible to work with these kinds of types with generics? If so, how?
Combine your two attempted interfaces together:
type Shape[ST any] interface {
*Circle | *Square
Bigger() ST
Smaller() ST
}
And then instantiate the constraint of process with the type parameter itself:
func process[ST Shape[ST]](s ST) ST {
return s.Bigger().Smaller()
}
Adding the union element *Circle | *Square into Shape[ST any] means that only those two types will be able to implement the interface
Then using the type parameter in the method signature, like Bigger() ST, means that whichever type is passed has a method that returns itself.
If you want to keep ShapeType as a separated interface, you can write Shape as:
type Shape[ST any] interface {
ShapeType
Bigger() ST
Smaller() ST
}
You can also use process method with type inference, without any issue:
func main() {
c1 := NewCircle(3)
c2 := process(c1)
fmt.Println(c2.Radius()) // prints 3 as expected
fmt.Printf("%T\n", c2) // *main.Circle
s1 := NewSquare(6)
s2 := process(s1)
fmt.Println(s2.Side()) // prints 6 as expected
fmt.Printf("%T\n", s2) // *main.Square
}
Final playground: https://go.dev/play/p/_mR4wkxXupH
I'm trying to refactor some golang source code, and I would like to use interfaces but I find huge difficulties (bear with me, I'm a hard-core C/C++ orphan)
I extracted a small sample exhibiting the error I get
package main
import "fmt"
type LogProcessor interface {
Init() int
}
type MyProcessor struct {
a int
}
func (m MyProcessor) Init() int {
return m.a
}
func main() {
t := &(MyProcessor{2})
var p *LogProcessor = &(MyProcessor{4}) //!!!fails!!!
fmt.Println((*t).Init(), (*p).Init())
}
Why is the second assignment failing?
Adding the code modified to demonstrate what I was trying to do. I thought interfaces where more similar to C++ classes. My fault. Still learning
package main
import "fmt"
type LogProcessor interface {
Init() int
}
type MyProcessor struct {
a int
}
func (m *MyProcessor) Init() int {
m.a++
return m.a
}
func main() {
t := &(MyProcessor{2})
m := MyProcessor{4}
var p LogProcessor = &m
fmt.Println(t.Init(), p.Init())
fmt.Println(t.a, m.a)
}
The expression MyProcessor{2} is a composite literal. It is valid to take the address of a composite literal, and it will be of type *MyProcessor.
So here:
t := &(MyProcessor{2})
Type of t will be *MyProcessor.
Your failing line:
var p *LogProcessor = &(MyProcessor{4}) //!!!fails!!!
The type of the expression on the right hand side is again *MyProcessor. It's a pointer to a concrete type. The type of p is *LogProcessor, it's a pointer to another type. Assignability rules don't apply here, so the value is simply not assignable to the variable p.
Note that there is an assignability rule:
A value x is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:
...
T is an interface type and x implements T.
In your example p's type is not an interface type, but a pointer to interface. You rarely (if ever) need this in go.
Instead if you use "just" the interface type:
var p LogProcessor = &(MyProcessor{4}) // This works!
This works because the concrete type *MyProcessor implements the interface type LogProcessor. You also don't need the parenthesis, you may simply use &MyProcessor{4}.
Then of course you can't dereference p (as it's not a pointer), so you have to use p.Init().
it is failing because you are using a pointer to an interface, if you remove the pointer to LogProcessor for var p it works
package main
import "fmt"
type LogProcessor interface {
Init() int
}
type MyProcessor struct {
a int
}
func (m MyProcessor) Init() int {
return 2
}
func main() {
t := &(MyProcessor{2})
var p LogProcessor = MyProcessor{4} // works without a pointer to the interface type
fmt.Println((*t).Init(), (p).Init())
}
//Creating a structure
type Vertex struct {
X, Y int
}
//Using Add() to add an element to the slice of structure, v
func (v []Vertex) Add() {
v = append(v, Vertex{2,3})
}
func main() {
v:= make([]Vertex, 2, 2) //Creating a slice of Vertex struct type
v.Add()
fmt.Println(v)
}
The go tour site returns the following errors:
invalid receiver type []Vertex ([]Vertex is not a defined type)
v.Add undefined (type []Vertex has no field or method Add)
Could someone help me with where exactly is it that I am going wrong
When you define a method, the receiver must be a named type, or a pointer to a named type.
So func (v []Vertex) Add() { ... } isn't valid because []Vertex isn't a named type or a pointer to a named type.
If you wish to use a method on a slice a vertices, you need a new type. For example:
type Vertices []Vertex
func (v *Vertices) Add() {
*v = append(*v, Vertex{2, 3})
}
The whole program will be like this:
package main
import "fmt"
type Vertex struct {
X, Y int
}
type Vertices []Vertex
func (v *Vertices) Add() {
*v = append(*v, Vertex{2, 3})
}
func main() {
v := make([]Vertex, 2, 2) //Creating a slice of Vertex struct type
(*Vertices)(&v).Add()
fmt.Println(v)
}
//Creating a structure
type Vertex struct {
X, Y int
}
type Verices struct{
Vertices []Vertex
}
func (v *Verices) Add() {
v.Vertices = append(v.Vertices, Vertex{2,3})
}
func main() {
v:= Verices{}
v.Add()
fmt.Println(v)
}
You can't call Add on the slice and you can't define a method on that as well, but you can wrap slice in a struct and define methods on top of that.
See in Action:
https://play.golang.org/p/NHPYAdGrGtp
https://play.golang.org/p/nvEQVOQeg7-
Why assigning a method to function handler works.
shortlink: https://play.golang.org/p/UEYGCpMgyV6
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func Abs() float64 {
return math.Sqrt(10)
}
func AbsFloat(f float64) float64 {
return math.Sqrt(f)
}
func main() {
v := Vertex{3, 4}
// Assigning Abs() to func handler.
var absFunc func() float64 = Abs
fmt.Println(absFunc())
// Wrong type as expected.
//absFunc = AbsFloat
// Assigning method to func handler works, why ?
absFunc = v.Abs
// Changing receiver args. Useful for unittesting but why/how this works ?
v.X = 1
v.Y = 1
fmt.Println(absFunc())
}
Is it a Golang type checking 'feature' that func()float64 == func(T)float64 or something else ? Please help to understand this concept.
v.Abs is a method value, and quoting from the spec:
The method value x.M is a function value that is callable with the same arguments as a method call of x.M.
A method value has a function type with identical parameter and result types as the method without the receiver.
Given the setup in the 54th slide of the golang tour:
type Abser interface {
Abs() float64
}
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
Why can't a method also be defined for the struct as well as the pointer to the struct? That is:
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
Defining this gives the following error:
prog.go:41: method redeclared: Vertex.Abs
method(*Vertex) func() float64
method(Vertex) func() float64
It can. Just define it on the struct and not the pointer. It will resolve both ways
Method Sets
The method set of the corresponding pointer type *T is the set of all
methods with receiver *T or T (that is, it also contains the method
set of T)
Try live: http://play.golang.org/p/PsNUerVyqp
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{5, 10}
v_ptr := &v
fmt.Println(v.Abs())
fmt.Println(v_ptr.Abs())
}
Update: As per comments I have created an extra example that actually makes use of the Abser interface to illustrate that both the value and the pointer satisfy the interface.
https://play.golang.org/p/Mls0d7_l4_t
While considering for example:
type T U
func (t *T) M() int { return 1 }
var t T
...we can now invoke M() on t by writing t.M() as the language permits to call a method with a pointer receiver even on its underlying (non pointer) typed instances, i.e. it becomes equivalent to (&t).M().
If it will be permitted to now additionaly define:
func (t T) M() int { return 2 }
...then there's no way to tell what is now t.M() supposed to return.