Defining a generics constraint on type fields [duplicate] - go

I am trying to define a generic function in Go that accepts values that have certain fields, for example, ID int. I have tried several approaches but none seems to work. Here is an example of what I have tried.
package main
import (
"fmt"
)
func Print[T IDer](s T) {
fmt.Print(s.ID)
}
func main() {
Print(Person{3, "Test"})
}
type IDer interface {
~struct{ ID int }
}
type Person struct {
ID int
Name string
}
type Store struct {
ID int
Domain string
}
And here is the playground link: https://gotipplay.golang.org/p/2I4RsUCwagF
In the example above, I want to guarantee every value passed to the Print function has a property ID int, which is also accessible in the function. Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?

Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?
No, you have to define the method in an interface.
The generics implementation in Go 1.18 doesn't have support for structural types, even though the original type parameters proposal suggests it would. For accessing common fields in a union instead see also this explanation.
Although, I think it's worth it to point out a misconception that can easily arise from your example: the meaning of the approximation ~T (tilde-type) means "the set of types whose underlying type is T.
Now, when you write:
~struct{ ID int }
this means types whose underlying type is exactly struct{ ID int }. No matter what, this does not include structs that have the field ID int and something else. E.g. the underlying type of type Foo struct { ID int; Name string } is struct { ID int; Name string }, and not struct{ ID int }, so that wouldn't satisfy the constraint anyway.
The current time param implementation doesn't have syntax to specify partial struct types. I recall a proposal to add field terms in interface constraints (along with type terms and methods), something on the line:
type IDer interface {
ID int
}
which would enable what you are trying to do without breaking the meaning of the tilde ~. But this won't be included in Go 1.18.

Related

Get the type name of a generic struct without type parameters

Say I have a generic struct called foo and I create two objects from it. I can determine the concrete type of each using reflect.TypeOf(), like so:
package main
import (
"fmt"
"reflect"
)
type foo[T any] struct {
data T
}
func main() {
a := foo[string]{"cheese"}
b := foo[int]{42}
fmt.Println(reflect.TypeOf(a))
fmt.Println(reflect.TypeOf(b))
}
// main.foo[string]
// main.foo[int]
What I am interested in is determining just the generic type of these objects (i.e., foo) and not the concrete type (i.e., foo[string] and foo[int]). Is this possible or do I need to manually extract the generic type from these strings (e.g., with regex)?
Edit
Regex might look something like this:
func GetGenericType(x any) string {
// Get type as a string
s := reflect.TypeOf(x).String()
// Regex to run
r := regexp.MustCompile(`\.(.*)\[`)
// Return capture
return r.FindStringSubmatch(s)[1]
}
fmt.Println(GetGenericType(a))
fmt.Println(GetGenericType(b))
// foo
// foo
I've also seen this question but this doesn't answer this question because it gives the concrete type (i.e., main.foo[string]) rather than the generic type (i.e., foo).
Reflection doesn't see the name of the "base" generic type, because at run time that base type doesn't exist.
The relevant passage from the Go spec is Instantiations:
Instantiating a type results in a new non-generic named type; instantiating a function produces a new non-generic function.
So when you write:
b := foo[int]{42}
name := reflect.TypeOf(b).Name()
the name of that type is precisely foo[int].
It's worth noting that the identifier foo without the type parameter list is relevant at compile time, because it prevents you from redeclaring it in the same package. Type definitions:
A type definition creates a new, distinct type with the same
underlying type and operations as the given type and binds an
identifier, the type name, to it.
TypeDef = identifier [ TypeParameters ] Type .
But instantiations, as defined above, result in a new named type which is different than foo; and at run time when you can use reflection, you deal with instantiations only.
In conclusion, I think your solution with regex is acceptable, until some helper function is added to the stdlib (if ever). Reposting it here for clarity:
func GetGenericType(x any) string {
// Get type as a string
s := reflect.TypeOf(x).String()
// Regex to run
r := regexp.MustCompile(`\.(.*)\[`)
// Return capture
return r.FindStringSubmatch(s)[1]
}
Just keep in mind the difference between Type.String() and Type.Name(): any type can have a string representation, but only named types have a name. (Obviously, right?). So for example if you wrote:
b := &foo[int]{42}
then the type of b is *foo[int], which is an anonymous composite type, and Name() returns an empty string.

GoLang - Generics instead of interface to return different types [duplicate]

This question already has answers here:
How to assign or return generic T that is constrained by union?
(2 answers)
Closed 5 months ago.
I read a lot of docs but I couldn't find if I'm able to do it.
Is there a way to do something like this in Go without use an interface as return type?
Playground example
package main
import "fmt"
type Foo struct {
Id uint
FooField string
}
type Bar struct {
Id uint
BarField string
}
type Model interface {
Foo | Bar
}
func build[T Model](s string) T {
if s == "foo" {
fmt.Println(s)
return Foo{}
}
fmt.Println(s)
return Bar{}
}
func main() {
build("foo")
}
No, at least not how you've shown here.
Go is a statically typed language, meaning that the semantic data types of values do not depend on any dynamic values in the program.
Interfaces are not an exception to this, but an extension. From the language specification (abridged):
The static type (or just type) of a variable is the type given in its declaration. Variables of interface type also have a distinct dynamic type, which is the (non-interface) type of the value assigned to the variable at run time.
Here's an alternate version that would work.
func build[T Model]() T {
var x T
return x
}
func main() {
// VVV explicitly passing the type parameter, not a string
build[Foo]()
}
The reason why this is valid and yours isn't, is because now the return type only depends on the type parameter provided, which is always static. Type parameters are often omitted entirely when they can be inferred by the compiler, but in cases like this, it's most correct to list them explicitly.

Is there a way we can ensure passed values have certain fields using generics?

I am trying to define a generic function in Go that accepts values that have certain fields, for example, ID int. I have tried several approaches but none seems to work. Here is an example of what I have tried.
package main
import (
"fmt"
)
func Print[T IDer](s T) {
fmt.Print(s.ID)
}
func main() {
Print(Person{3, "Test"})
}
type IDer interface {
~struct{ ID int }
}
type Person struct {
ID int
Name string
}
type Store struct {
ID int
Domain string
}
And here is the playground link: https://gotipplay.golang.org/p/2I4RsUCwagF
In the example above, I want to guarantee every value passed to the Print function has a property ID int, which is also accessible in the function. Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?
Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?
No, you have to define the method in an interface.
The generics implementation in Go 1.18 doesn't have support for structural types, even though the original type parameters proposal suggests it would. For accessing common fields in a union instead see also this explanation.
Although, I think it's worth it to point out a misconception that can easily arise from your example: the meaning of the approximation ~T (tilde-type) means "the set of types whose underlying type is T.
Now, when you write:
~struct{ ID int }
this means types whose underlying type is exactly struct{ ID int }. No matter what, this does not include structs that have the field ID int and something else. E.g. the underlying type of type Foo struct { ID int; Name string } is struct { ID int; Name string }, and not struct{ ID int }, so that wouldn't satisfy the constraint anyway.
The current time param implementation doesn't have syntax to specify partial struct types. I recall a proposal to add field terms in interface constraints (along with type terms and methods), something on the line:
type IDer interface {
ID int
}
which would enable what you are trying to do without breaking the meaning of the tilde ~. But this won't be included in Go 1.18.

Golang - add "inheritance" to structs

I would like to optimize my code, I have then the following situation:
I have a general struct where only one field gives the specification, let say a cache struct example:
# the main cache struct
type Cache struct {
name string
memory_cache map[string]interface{}
mutex *sync.Mutex
... ...
# common fields
}
# an element stored in the Cache.memory_cache map
type ElementA {
name string
count int64
}
# an element stored in the Cache.memory_cache map
type ElementB {
name string
tags []string
}
My current solution follow the previously definition and I create a cache for each element (it must be so: one cache per element):
var cache_for_element_A Cache{}
var cache_for_element_B Cache{}
But in this way I must always cast the memory_cache when reading, even if I know already what is the content (then no cast-case should be needed).
The following code do what I would like to have, but it defines twice a lot of redundants/commons fields, for this reason I would like to find another solution.
type CacheForA struct {
name string
memory_cache map[string]ElementA{}
mutex *sync.Mutex
... ...
# common fields
}
type CacheForB struct {
name string
memory_cache map[string]ElementB{}
mutex *sync.Mutex
... ...
# common fields
}
Then, is it possible to define a field in the struct (more precisely Cache.memory_cache) that can be further defined when the declaration occurs and without using interface?
Go doesn't have generics, so there's no simple way of doing this, like you would in Java for instance (class Cache<T>()....).
One thing you can do is wrap your cache with a small typed function that just fetches objects from a generic cache and converts the interface to the right type. This just saves you from writing the interface conversion over and over in your code.
type ElemACache struct {
Cache
}
func (c *ElemeACache)Get(key string) ElemeA {
return c.Cache.Get(key).(ElemeA) //of course add checks here
}
Struct embedding is the main thing you are looking for I think:
type Cache struct {
name string
mutex *sync.Mutex
}
type CacheA struct {
Cache
memory_cache map[string]ElementA{}
}
Then you make a type of interface, say "Cacher" that has a set of methods for the things you need to do with your various caches (cacheA, CacheB). Create those methods for CacheA, CacheB, and assertions are only needed only for the return type:
type Cacher interface {
GetItem(string) (interface{}, error)
}
If all your CacheFor types have that GetItem method, the interface will be fulfilled.
Still a fair amount of boilerplate, but this reduces the problem with redundancy in struct definitions. There are code generation tools if you don't want to type the boiler plate.

Is it necessary to use a type assertion to access the values of a type returned by interface?

When I have a function that returns an interface type, the returned value doesn't work as I would expect. That is, it acts strictly as the defined interface, and to access the methods and values not defined in the interface, I have to do a type assertion. Why?
Consider the following sample code:
package main
import (
"fmt"
)
type Frobnicator interface {
Frobnicate()
}
type Foo struct {
Value string
}
func (f *Foo) Frobnicate() {
fmt.Printf("The value is %s\n", f.Value)
}
func fooFactory () Frobnicator {
return &Foo{"chicken"}
}
func main() {
foo := fooFactory( )
foo.Frobnicate()
// foo.Value undefined (type Frobnicator has no field or method Value)
// fmt.Printf("foo value = %s\n", foo.Value)
bar := foo.(*Foo)
fmt.Printf("bar value = %s\n", bar.Value)
}
Is there a better, easier, more idiomatic way to get at foo.Value? Or is a type assertion really the best way?
Not sure what to answer here. Maybe there is a misconception what interface types are. Interface types are absolutely normal types. And you can do with a interface value what the interface says: Invoke the interface methods. For a struct type you may access fields and invoke interface methods as defined by the struct type. So everything is plain and simple: A type allows what it allows, no matter whether interface or struct.
It now happens that a value of interface type may contain some struct value (say). Up to now this is hidden. Type asserting reveals the struct value (and there is no more interface). You may hide an other struct value in the interface (given it implements the right methods) this might not have a Value field. This makes it clear that you cannot access the Value field without a type assertion, because it might not be there.
If you need access to inner value of interface implementation (Value), you must either expose it via interface itself or do a type assertion. That's because nothing in Frobnicator suggests whether it's Foo or some other implementing struct.
It is not different than many other languages. In Java you will have to cast also under similar circumstances.

Resources