This question already has answers here:
Why a generic can't be assigned to another even if their type arguments can?
(1 answer)
Can I construct a slice of a generic type with different type parameters?
(2 answers)
Go generic container with different runtime types
(1 answer)
Closed 10 months ago.
This post was edited and submitted for review 10 months ago and failed to reopen the post:
Original close reason(s) were not resolved
I want to be able to enforce similarity between two fields of a struct but also have several of these structs in a map or slice.
Here's a simplified example of my problem:
package main
type foo[T any] struct {
f func() T
v T
}
type bar struct {
x []*foo[any]
}
func baz[T any](b *bar, f func() T) {
b.x = append(b.x, &foo[any]{f: f})
}
func main() {
var b bar
baz(&b, func() int { return 0 })
}
The compiler complains
./prog.go:13:33: cannot use f (variable of type func() T) as type func() any in struct literal
The funny thing is that this can work if I didn't need to have a function pointer in the struct. See https://go.dev/play/p/qXTmaa9PuVe
So, is there a way for me to turn a T into an any?
I know I could do this with interface{}s and use reflect to enforce what I want, but I'm sure it's possible with only generics.
The context in case there is a way around my problem is that I'm making a flag package. The important structs look like this:
type flag[T any] struct {
value T
parse func(in string) (T, error)
// Other fields removed for simplicity...
}
type FlagSet struct {
// could flag[any] be replaced with a better answer?
flags map[string]*flag[any]
// Other fields removed for simplicity...
}
The question was closed so I have to put the answer to the second part of my question here
could flag[any] be replaced with a better answer?
The answer to the above is yes.
Solution:
Originally I though something like: "a func() fits a func() and an any fits a T so why can't I have a func() T fit a func() any?" Of course the reason is a func() any is not an any and so it cannot hold a func() T.
Instead, you can do the following:
package main
type foo[T any] struct {
f func() T
v T
}
func (f *foo[_]) set() {
f.v = f.f()
}
type anyfoo interface {
set()
}
type bar struct {
x []anyfoo
}
func baz[T any](b *bar, f func() T) {
b.x = append(b.x, &foo[T]{f: f})
}
func main() {
var b bar
baz(&b, func() int { return 0 })
}
but also have several of these structs in a map or slice
You cannot do this (in a type safe way). All values of e.g. a slice must have the same element type. If you want to store different ones you have to resort to interface{} and type switch later.
If you use the correct technical term parametric polymorphism instead of "generics" which doesn't explain what is going on you will see why func(T) and func(any) are different, unconvertable types.
So, is there a way for me to turn a T into an any?
No, there was no pre-"generics" way and there is no post-"generics" way. It helps to think of "turn into" as what Go allows: "type conversion" and "assignment". You can assign any variable of type T to a variable of type any.
You might overcome your issue by using an adaptor function (closure):
w := func(a any){ f(a.(T)) }
b.x = append(b.x, &foo[any]{w})
Related
This question already has answers here:
Type converting slices of interfaces
(9 answers)
Cannot convert []string to []interface {}
(7 answers)
Cannot use args (type []string) as type []interface {} [duplicate]
(1 answer)
slice of struct != slice of interface it implements?
(6 answers)
Closed 7 days ago.
I have two types AssetClips and Videos that implement the interface call timelineClip.
I wanted to pass a []AssetClips or a []Videos to a function that take as argument a []timelineClip but the compiler was complaining, I don't really understand why. I ended up doing a for loop to convert my []AssetClips and my []Videos to []timelineClip
Is it necessary and is there a more elegant way of doing that?
// myFunctionThatTakesASliceOfTimelineClips(assetClips) is not possible
// myFunctionThatTakesASliceOfTimelineClips(videos) is not possible
var timelineClips []timelineClip
for _, assetClip := range assetClips {
timelineClips = append(timelineClips, assetClip)
}
for _, video := range videos {
timelineClips = append(timelineClips, video)
}
myFunctionThatTakesASliceOfTimelineClips(timelineClips)
It is necessary, and this is an elegant way to do it.
This is necessary because the mechanics of passing a slice of interface is different from the mechanics of passing a slice of structs. Each element of a slice of structs is a copy of the struct itself, whereas the elements of an interface is an interface pointing to an instance of a struct, together with its type.
If you want to avoid copying, you could use a generics for this. In short, you just change signature of
func myFunctionThatTakesASliceOfTimelineClips(timelineClips []timelineClip)
to
func myFunctionThatTakesASliceOfTimelineClips[T timelineClip](timelineClips []T)
As an example:
https://go.dev/play/p/FTj8rMYq9GF
package main
import "fmt"
type Exampler interface {
Example()
}
type A struct{}
type B struct{}
func (a A) Example() {
fmt.Println("it worked")
}
func (b B) Example() {
fmt.Println("it worked")
}
func DoExample[T Exampler](tt []T) {
for _, t := range tt {
t.Example()
}
}
func main() {
aa := []A{{}}
bb := []B{{}}
DoExample(aa)
DoExample(bb)
}
This question already has answers here:
Type converting slices of interfaces
(9 answers)
In Go, how do I pass a slice of interface to something that expects slice of a different compatible interface? [duplicate]
(1 answer)
How can I cast from []interface{} to []int? [duplicate]
(1 answer)
Cannot use args (type []string) as type []interface {} [duplicate]
(1 answer)
Convert map[interface {}]interface {} to map[string]string
(3 answers)
Closed 7 months ago.
Assuming only arrays are passed as arguments to the arr parameter, I would like each call of unpackArray() to return the argument casted from its original array type to type []any.
package main
func unpackArray(arr any) []any {
return arr.([]any)
}
func main() {
myArr1 := []string {"Hey"}
myArr2 := []int {60}
unpackArray(myArr1)
unpackArray(myArr2)
}
However, this code yields error panic: interface conversion: interface {} is []string, not []interface {}. So it is not allowing me to cast an interface whose static type is not type []any to type []any.
So, given I know that arr's static type is some type of array, and without changing the arr parameter's initialization type from any, how could I convert arr to type []any using this function?
(I am encountering the same problem with maps where I cannot cast from an arbitrary map type to type map[any]any, but I am guessing the solution to this issue would be similar to the solution for arrays.)
Go does not have a builtin "cast" like this, but you can write a function to do it.
You may use reflection to convert a slice of any type to []any:
func unpackArray(s any) []any {
v := reflect.ValueOf(s)
r := make([]any, v.Len())
for i := 0; i < v.Len(); i++ {
r[i] = v.Index(i).Interface()
}
return r
}
You can also use generics in Go 1.18 or later:
func unpackArray[S ~[]E, E any](s S) []any {
r := make([]any, len(s))
for i, e := range s {
r[i] = e
}
return r
}
Both versions of these functions work as requested in the question:
myArr1 := []string {"Hey"}
myArr2 := []int {60}
unpackArray(myArr1)
unpackArray(myArr2)
Notes:
Go does not have "cast" like some other languages. Go has the somewhat related type assertion and conversion features.
The expression arr.([]any) is a type assertion. The expression asserts that the concrete value in the interface arr has type []any. The expression does not do any conversion.
The code in the question uses slices , not arrays as written in the title.
It's not possible to do that directly, because it's not the same thing.
any is the same of interface{} and each interface{} is two-pointers (the first one is the "metadata"/"type-information" and the second one the pointer to the original data).
If you have []uint{60, 42} you have one slice that each element is 8-byte (considering 64bits). So, if you force it to be []any, each element now take 16 bytes, that breaks everything. You can do it using unsafe.
The only way to "cast" is copying the information, so, you can create a new slice of []any and then append each value into that new slice.
One example of copying is:
// You can edit this code!
package main
func unpackArray[T any](arr any) (r []any) {
o := arr.([]T)
r = make([]any, len(o))
for i, v := range o {
r[i] = any(v)
}
return r
}
func main() {
myArr1 := []string{"Hey"}
myArr2 := []int{60}
unpackArray[string](myArr1)
unpackArray[int](myArr2)
}
However, that doesn't make so much sense, since you can use generics in another way.
This question already has an answer here:
cannot use function (type func()) as type in argument
(1 answer)
Closed 2 years ago.
I have two functions which return pointers to two separate structs conforming to the same interface. How can I put the functions in the same map? I came up with creating wrapper functions (getFooer in the example) to make the types check. Is there a better way? What are the rules that make types check for the type conversion in the getFooer function, but not for the type conversion in the commented out line in main?
package main
import (
"fmt"
)
type Fooer interface {
Foo()
}
type A struct {
}
func (a *A) Foo() {
}
var a A = A{}
func getA() (*A) {
return &a
}
func getFooer() (Fooer) {
return getA()
}
func main() {
var f func() (Fooer)
// f = getA // /tmp/foo.go:29:7: cannot use getA (type func() *A) as type func() Fooer in assignment
f = getFooer
fmt.Println(f)
}
How can I put the functions in the same map?
You cannot, they have different types.
I came up with creating wrapper functions (getFooer in the example) to make the types check. Is there a better way?
Basically: No. You could modify the signature of e.g. getA to getA() Fooer but that would require type assertions back to *A if you need an A.
What are the rules that make types check for the type conversion in the getFooer function, but not for the type conversion in the commented out line in main?
Dead simple: A function getA() *A hase type func() *A and this type is different from func() Fooer (no covariance). Functin types must match literally. You can return a *A as a Fooer because you can assign a *A to a variable of type Fooer. The getA yields a *A and this *A is assigned to the return value of getFooer which is a Fooer.
This question already has answers here:
Can I type assert a slice of interface values?
(2 answers)
Closed 3 years ago.
I have a type that implements the stringer interface
// RowID stores the ID of a single row in a table
type RowID []string
// String implements Stringer interface for RowID
func (r RowID) String() string {
return fmt.Sprintf("[%s]", strings.Join(r, ", "))
}
And I have a function that I want to pass a slice of this type (or any other type that implements the Stringer interface) to.
// PrintChanges ...
func PrintChanges(ids []fmt.Stringer) {
for _, id := range ids {
fmt.Println(id)
}
}
However, The go compiler gives me an error:
cannot use rowIDs (type []RowID) as type []fmt.Stringer in argument to PrintChanges
I can pass a RowID to a func that accepts a single fmt.Stringer
func PrintChange(id fmt.Stringer) {
fmt.Println(id)
}
...
PrintChange(RowID{"1", "24"})
But for some reason I am not able to pass a slice of RowID to a func that accepts a slice of fmt.Stringer. What am I missing?
Go Playground
Keep it simple
It is considered okay by professional Go programmers to repeat functions like this for every type, or to have a for loop over every slice you want to print. This is because Go aims to be as easy to read as possible, i.e. a person who reads a chunk of code for the first time should not be asking questions like "which function overload will this function call go to" (common pitfall in C++, Go does not have function overloads). So you can just write in main():
Playground: https://ideone.com/IL3rGR
for _, id := range rowIDs { fmt.Println(id) }
Simple and concise.
Note that fmt.Println(id) does not call your String() function
This is because the fmt library uses the reflect library and hardcodes behavior for the string type, which you are trying to replace. RowID instances are also string instances, the library always prefers string over its type aliases. I would say it is a bug in the library:
Library source: https://golang.org/src/fmt/print.go#L649
// Some types can be done without reflection.
switch f := arg.(type) {
...
case string:
p.fmtString(f, verb)
If you really want to
You can use a function that takes an interface{} and makes a runtime reflect type cast to a slice of Stringers. Note that this means you will not see type mismatches during compilation, only in runtime:
Playground: https://ideone.com/vlrBP9
func castToStringerSlice(iface interface{}) ([]fmt.Stringer, bool /* ok */) {
if reflect.TypeOf(iface).Kind() != reflect.Slice {
return nil, false
}
v := reflect.ValueOf(iface)
stringers := make([]fmt.Stringer, v.Len())
for i := 0; i < v.Len(); i++ {
stringers[i] = v.Index(i)
}
return stringers, true
}
func PrintChanges(iface_ids interface{}) {
ids, ok := castToStringerSlice(iface_ids)
if !ok {
log.Fatal(errors.New("the argument to PrintChanges must be a slice of Stringers"))
}
for _, id := range ids {
fmt.Println(id)
}
}
Resources:
Go documentation: Why does Go not support overloading of methods and operators?
Go documentation: Why does Go not have generic types?
Stack Overflow: Express function that takes any slice
Stack Overflow: Range over interface{} which stores a slice
Go documentation: Package reflect
This question already has answers here:
Appending to go lang slice using reflection
(2 answers)
Closed 8 months ago.
I'm relatively new to go. I'm trying to write a generic "appender" function. This is a simplification, but its an attempt to create a clean interface for processing some lists. Specifically, I have questions about the two errors that this generates:
package main
type GenericFunc func() *interface{}
func Append(ints interface{}, f GenericFunc) {
ints = append(ints, f())
}
func ReturnInt() *int {
i := 1
return &i
}
func main() {
var ints []*int
Append(ints, ReturnInt)
}
Playground
prog.go:5:18: first argument to append must be slice; have interface
{} prog.go:15:11: cannot use ReturnInt (type func() *int) as type
GenericFunc in argument to Append
Why can't ReturnInt be of type GenericFunc? If this doesn't work, I'm not understanding how interface{} can be used with functions at all.. can it?
How can you accept a "generic" slice and append to it using reflection? This would involve checking that GenericFunc returns the same type that the slice is, but after that appending should be possible.
The types func() *interface{} (type type of GenericFunc) and (type func() *int) (the type of ReturnInt) are different types. One returns a *interface{}. The other returns a *int. The types are not assignable to each other.
Use this function to generically append the result of a function to a slice:
func Append(sp interface{}, f interface{}) {
s := reflect.ValueOf(sp).Elem()
s.Set(reflect.Append(s, reflect.ValueOf(f).Call(nil)[0]))
}
Call it like this:
var ints []*int
Append(&ints, ReturnInt)
The function will panic if the argument is not a pointer to a slice or the function does not return a value assignable to a slice element.
playground example