golang generics with constraints on pointer reciever [duplicate] - go

Probably a golang beginner's question :)
I'm facing following compiler error when trying to compile the code below.
I want to implement an object store for different types (here A and B) sharing a common ID field. Following the DRY idea, I want to implement the store using generics.
When adding an object, I want to set its ID field using the GS interface (the actual code is a bit more complex of course), but the compiler does not want me to do that.
./prog.go:29:7: item.SetId undefined (type *T is pointer to type parameter, not type parameter)
./prog.go:34:24: A does not implement GS (SetId method has pointer receiver)
Is there a recommended way to solve this? Thanks in advance!!
package main
import "fmt"
type A struct {
ID string
AMember string
}
type B struct {
ID string
BMember string
}
type GS interface {
Id() string
SetId(string)
}
func (s A) Id() string { return s.ID }
func (s *A) SetId(i string) { s.ID = i }
func (s B) Id() string { return s.ID }
func (s *B) SetId(i string) { s.ID = i }
type MyStore[T GS] struct {
values map[string]*T
}
func (s *MyStore[T]) add(item *T) {
item.SetId("aa")
s.values["aa"] = item
}
func main() {
var storeA = &MyStore[A]{}
storeA.values = make(map[string]*A)
a := &A{}
storeA.add(a)
fmt.Println(a.Id())
}

About using *T
In short, a type parameter is not its constraint. The constraint only determines what operations are available on T, it doesn't imply anything about *T, which is now just an unnamed pointer type. This is the meaning of:
type *T is pointer to type parameter, not type parameter
As a consequence, as in your case, the method set of *T does not automatically include pointer receiver methods declared on the T's concrete type A, and it does not implement interfaces that would be implemented by *A.
You'd have to make that explicit to the compiler by setting additional constraints. In a simplified form it would be something like:
func Foo[T any, PT interface { SetId(string); *T}](v T) {}
You can find more extensive examples and variations on this use case here:
Go 1.18 Generics how to define a new-able type parameter with interface
How can I instantiate a new pointer of type argument with generic Go?
What is the generic type for a pointer that implements an interface?
About implementing constraints
The reason this instantiation &MyStore[A]{} fails is clearly reported by the error message:
A does not implement GS (SetId method has pointer receiver)
In other words SetId() is declared on *A, and not A. Therefore you should instantiate MyStore with *A:
var storeA = &MyStore[*A]{}
Then change the occurrences of *T in the struct/method definition to T:
type MyStore[T GS] struct {
values map[string]T // just T instead of *T
}
func (s *MyStore[T]) add(item T) {
}
Upon instantiation with *A the type of the field would become equivalent to map[string]*A thus making the assignment storeA.values = make(map[string]*A) valid, and the method signature to add(item *A) thus allowing storeA.add(&A{}).
Fixed playground: https://gotipplay.golang.org/p/dcUVJ5YQK_b

Related

Golang: Generic struct receiver gets error: undefined (type T has no field or method) [duplicate]

I would like to make the following code compile. My understanding from reading the Type Parameters Proposal (Go Generics) is that this should work, but I must be missing something.
package main
import "fmt"
func main() {
s := Struct{A: "Hello World!"}
PrintA(s)
}
func PrintA[T Type](v T) {
fmt.Printf("%s\n", v.A)
}
type Type interface {
struct{ A string }
}
type Struct struct {
A string
}
func (s Struct) String() string {
return s.A
}
The error I get is:
./prog.go:7:8: Struct does not implement Type (possibly missing ~ for struct{A string} in constraint Type)
./prog.go:11:23: v.A undefined (type T has no field or method A)
I would like T to represent all structs with a particular field of a particular type. Adding ~ did not help.
Here's an example from the proposal that was implemented and is part of the latest Go beta release.
type structField interface {
struct { a int; x int } |
struct { b int; x float64 } |
struct { c int; x uint64 }
}
https://go.dev/play/p/KZh2swZuD2m?v=gotip
Field access has been disabled for Go 1.18 (still disabled in Go 1.19). The Go 1.18 release notes mention this:
The current generics implementation has the following known limitations:
[...]
The Go compiler does not support accessing a struct field x.f where x is of type parameter type even if all types in the type parameter's type set have a field f. We may remove this restriction in Go 1.19.
The workaround for any struct type boils down to old boring interface-based polymorphism:
type Type interface {
GetA() string
}
func (s Struct) GetA() string {
return s.A
}
And at this point you don't even have to use the Type interface as a constraint. It can just be a plain interface type:
func PrintA(v Type) {
fmt.Printf("%s\n", v.GetA())
}
If you are ok with using this interface only as a constraint, you may add type elements to restrict which structs can implement it:
type Type interface {
StructFoo | StructBar
GetA() string
}
Use pointer types if you declared the methods with pointer receiver.
Old answer (not relevant anymore, only informative)
At some point in early 2022 while this feature was still in development, your example did work if you added ~:
type Type interface {
~struct{ A string }
}
but it only worked for structs exactly defined as struct{ A string } and nothing else. Defining a constraint that "represent[s] all structs with a particular field of a particular type" was never supported all along. See this answer for details.
Instead the example you quote from the proposal is about accessing a common field in a type set. By defining a union of structs:
type structField interface {
~struct { a int; x int } | ~struct { a int; x float64 }
}
you should be able to access the field a of such a type parameter, but again this wasn't implemented, as mentioned at the beginning of the answer. It used to work if all terms in the union had the same underlying type (example adapted from issue #48522).
This code doesn't compile anymore as of March 2022:
package main
import "fmt"
type Point struct {
X, Y int
}
type Rect struct {
X, Y int
}
func GetX[P Point | Rect] (p P) int {
return p.X
}
func main() {
p := Point{1, 2}
r := Rect{2, 3}
fmt.Println("X: %d %d", GetX(p), GetX(r)) // prints X: 1 2
}

Go with Generics: type *T is pointer to type parameter, not type parameter

Probably a golang beginner's question :)
I'm facing following compiler error when trying to compile the code below.
I want to implement an object store for different types (here A and B) sharing a common ID field. Following the DRY idea, I want to implement the store using generics.
When adding an object, I want to set its ID field using the GS interface (the actual code is a bit more complex of course), but the compiler does not want me to do that.
./prog.go:29:7: item.SetId undefined (type *T is pointer to type parameter, not type parameter)
./prog.go:34:24: A does not implement GS (SetId method has pointer receiver)
Is there a recommended way to solve this? Thanks in advance!!
package main
import "fmt"
type A struct {
ID string
AMember string
}
type B struct {
ID string
BMember string
}
type GS interface {
Id() string
SetId(string)
}
func (s A) Id() string { return s.ID }
func (s *A) SetId(i string) { s.ID = i }
func (s B) Id() string { return s.ID }
func (s *B) SetId(i string) { s.ID = i }
type MyStore[T GS] struct {
values map[string]*T
}
func (s *MyStore[T]) add(item *T) {
item.SetId("aa")
s.values["aa"] = item
}
func main() {
var storeA = &MyStore[A]{}
storeA.values = make(map[string]*A)
a := &A{}
storeA.add(a)
fmt.Println(a.Id())
}
About using *T
In short, a type parameter is not its constraint. The constraint only determines what operations are available on T, it doesn't imply anything about *T, which is now just an unnamed pointer type. This is the meaning of:
type *T is pointer to type parameter, not type parameter
As a consequence, as in your case, the method set of *T does not automatically include pointer receiver methods declared on the T's concrete type A, and it does not implement interfaces that would be implemented by *A.
You'd have to make that explicit to the compiler by setting additional constraints. In a simplified form it would be something like:
func Foo[T any, PT interface { SetId(string); *T}](v T) {}
You can find more extensive examples and variations on this use case here:
Go 1.18 Generics how to define a new-able type parameter with interface
How can I instantiate a new pointer of type argument with generic Go?
What is the generic type for a pointer that implements an interface?
About implementing constraints
The reason this instantiation &MyStore[A]{} fails is clearly reported by the error message:
A does not implement GS (SetId method has pointer receiver)
In other words SetId() is declared on *A, and not A. Therefore you should instantiate MyStore with *A:
var storeA = &MyStore[*A]{}
Then change the occurrences of *T in the struct/method definition to T:
type MyStore[T GS] struct {
values map[string]T // just T instead of *T
}
func (s *MyStore[T]) add(item T) {
}
Upon instantiation with *A the type of the field would become equivalent to map[string]*A thus making the assignment storeA.values = make(map[string]*A) valid, and the method signature to add(item *A) thus allowing storeA.add(&A{}).
Fixed playground: https://gotipplay.golang.org/p/dcUVJ5YQK_b

Create new object of typed value via Go (go 1.18) generics

I am playing with generics in beta release of go 1.18. Create function in example below should create new instance of *T (therefore *Apple). I tried to use reflect package for that, but without luck.
Can you please show me how I can change function Create from the example below so that it creates instance of T instead of returning nil and crashing my example?
type FruitFactory[T any] struct{}
func (f FruitFactory[T]) Create() *T {
//how to create non-nil fruit here?
return nil
}
type Apple struct {
color string
}
func example() {
appleFactory := FruitFactory[Apple]{}
apple := appleFactory.Create()
//panics because nil pointer access
apple.color = "red"
}
Since you are instantiating FruitFactory with a non-pointer type (Apple), you can just declare a typed variable and return its address:
func (f FruitFactory[T]) Create() *T {
var a T
return &a
}
Or:
func (f FruitFactory[T]) Create() *T {
return new(T)
}
Playground: https://gotipplay.golang.org/p/IJErmO1mrJh
If you want to instantiate FruitFactory with a pointer type and still avoid segmentation faults, things get more complicated. Basically you have to take advantage of type inference to declare a variable of the non-pointer type in the method body and convert that to the pointer type.
// constraining a type to its pointer type
type Ptr[T any] interface {
*T
}
// the first type param will match pointer types and infer U
type FruitFactory[T Ptr[U], U any] struct{}
func (f FruitFactory[T,U]) Create() T {
// declare var of non-pointer type. this is not nil!
var a U
// address it and convert to pointer type (still not nil)
return T(&a)
}
type Apple struct {
color string
}
func main() {
// instantiating with ptr type
appleFactory := FruitFactory[*Apple, Apple]{}
apple := appleFactory.Create()
// all good
apple.color = "red"
fmt.Println(apple) // &{red}
}
Playground: https://gotipplay.golang.org/p/07nUGI-xP0O
EDIT, March 2022: type inference for defined types has been disabled, so the second playground doesn't compile anymore. Leaving the original one for reference. You must supply all type parameters: FruitFactory[*Apple, Apple]{}, which does make it quite verbose. Type inference works normally for functions.

Pass struct for function that accepts a interface

I have the following code:
package main
type MyInterface interface {
Test()
}
type MyType struct {
}
func (m MyType) Test(){}
func AcceptInterface(i *MyInterface){
}
func main() {
object := &MyType{}
AcceptInterface(object)
}
I was expecting this to work, because MyType implements MyInterface, but I get:
cannot use object (type *MyType) as type *MyInterface in argument to
AcceptInterface: *MyInterface is pointer to interface, not interface
I tried doing type assertion: object.(MyInterface), but that doesn't work either.
How can I accomplish this?
As the error says,
cannot use object (type *MyType) as type *MyInterface in argument to AcceptInterface: *MyInterface is pointer to interface, not interface
This means that it is expecting an interface value, not a pointer.
If you change the pointers to values in your code (by removing the & and *), the program will run with no errors:
package main
type MyInterface interface {
Test()
}
type MyType struct {
}
func (m MyType) Test(){}
func AcceptInterface(i MyInterface){
}
func main() {
object := MyType{}
AcceptInterface(object)
}
Play it
Edit 1
If you still want to use a pointer as an argument, there are two important parts of the Go language to note
From the Go Spec on what exacly is a variable that fits an instance:
A variable of interface type can store a value of any type with a method set that is any superset of the interface.
From the Go Spec on what pointers being automatically dereferenced:
As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv [and] as with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp.
Those two points are important, because when combined they explain that pointers to variables can still fit instances. This is because the pointer's method set is automatically dereferenced by the Go compiler (and since the variable it is referencing can fit an instance, the pointer can, too)!
In action, this means that in order to see if a pointer fits an instance, you have to declare the instance as a value and the pointer as a pointer.
If you run this code:
package main
type MyInterface interface {
Test()
}
type MyType struct {
}
func (m MyType) Test() {}
func AcceptInterface(i MyInterface) {
}
func main() {
object := &MyType{}
AcceptInterface(object)
}
Play it
you will see that there are no errors! Notice how there is an & in the object declaration, but no * in the i declaration?
type MyInterface interface {
Test()
}
type MyType struct {
}
func (m MyType) Test(){}
Note: it means that MyType implemts the interface MyInterface, not MyType*.
You can use:
func (m *MyType) Test(){} //Notice here
func AcceptInterface(i *MyInterface){
}
func main() {
object := &MyType{}
AcceptInterface(object)
}
Use explicit typing if you want to pass a pointer to interface:
func main() {
var object MyInterface = MyType{}
AcceptInterface(&object)
}
I would not recommend using pointer interfaces as you would need to write code like (*i).Test() to call the interface pointer methods. The compiler do auto dereferencing for struct pointers, not so for interface pointers.

Polymorphism in Go - does it exist?

I am trying to make something real simple on Go: to have an interface with getter and setter methods. And it seems setter methods are not allowed.
Given this code:
package main
import "fmt"
type MyInterfacer interface {
Get() int
Set(i int)
}
type MyStruct struct {
data int
}
func (this MyStruct) Get() int {
return this.data
}
func (this MyStruct) Set(i int) {
this.data = i
}
func main() {
s := MyStruct{123}
fmt.Println(s.Get())
s.Set(456)
fmt.Println(s.Get())
var mi MyInterfacer = s
mi.Set(789)
fmt.Println(mi.Get())
}
Set method does not work, because in func (this MyStruct) Set(i int), this MyStruct is not a pointer, and the changes are lost as soon at the function exits. But making it this *MyStruct would not compile. Is there any workaround?
Here is a corrected version of your code (playground). This isn't exactly Polymorphism, but the use of an interface is good Go style.
package main
import "fmt"
type MyInterfacer interface {
Get() int
Set(i int)
}
type MyStruct struct {
data int
}
func (this *MyStruct) Get() int {
return this.data
}
func (this *MyStruct) Set(i int) {
this.data = i
}
func main() {
s := &MyStruct{123}
fmt.Println(s.Get())
s.Set(456)
fmt.Println(s.Get())
var mi MyInterfacer = s
mi.Set(789)
fmt.Println(mi.Get())
}
I once found this example of how to do polymorphism in Go:
http://play.golang.org/p/6Ip9scm4c3
package main
import "fmt"
type Talker interface {
Talk(words string)
}
type Cat struct {
name string
}
type Dog struct {
name string
}
func (c *Cat) Talk(words string) {
fmt.Printf("Cat " + c.name + " here: " + words + "\n")
}
func (d *Dog) Talk(words string) {
fmt.Printf("Dog " + d.name + " here: " + words + "\n")
}
func main() {
var t1, t2 Talker
t1 = &Cat{"Kit"}
t2 = &Dog{"Doug"}
t1.Talk("meow")
t2.Talk("woof")
}
To answer the question the in the title to post:
Go does not use classes, but provides many of the same features:
* message passing with methods
* automatic message delegation via embedding
* polymorphism via interfaces
* namespacing via exports
From: http://nathany.com/good/
Solving the code you supplied, I will leave to some more learned Gopher
###AD HOC polymophism
Ad hoc polymorphism is a general way of polymorphism implementation for statically typed languages. Polymorphism in Go is ad hoc polymorphism which is very close to Bjarne's Stroustrup definition:
Polymorphism – providing a single interface to entities of different types.
Interfaces
Go interface is really powerful tool designed specially for polymorphism implementation. Interface is a type abstraction (sets of methods) which provides a way to specify the behavior of an object: if something can do this, then it can be used here. Back to Straustrup's polymorphism definition: it is possible to use objects of different types as a type of a common interface if they implement the interface.
Playground with an example.
Parametric polymorphism
Wiki:
A function or a data type can be written generically so that it can handle values identically without depending on their type.
This kind of polymorphism is more regular for dynamically typed languages like Python or Ruby but Go implements it too! Go uses type empty interface interface{} for this purpose.
Type interface{}
From Tour Of Go:
The interface type that specifies zero methods is known as the empty interface:
interface{}
An empty interface may hold values of any type. Every type implements at least zero methods.
Empty interfaces are used by code that handles values of unknown type. For example, fmt.Print takes any number of arguments of type interface{}.
And it is possible to get particular type of an object with type assertion.
And again Tour Of Go:
A type assertion provides access to an interface value's underlying concrete value.
t := i.(T)
This statement asserts that the interface value i holds the concrete type T and assigns the underlying T value to the variable t.
There we have parametric polymorphism with static duck typing.

Resources