How to implement a generic slice appender? [duplicate] - go

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

Related

How to pass a slice of type A to a function that take a slice of interface that is implemented by A elegantly? [duplicate]

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)
}

How do you cast an array of unknown type to type []any? [duplicate]

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.

Convert func(T) to func(any) [duplicate]

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})

Assign "func returning pointer to struct" to var of type "func returning pointer to interface" [duplicate]

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.

In Go, how do I pass a slice of interface to something that expects slice of a different compatible interface? [duplicate]

This question already has answers here:
Type converting slices of interfaces
(9 answers)
Closed 6 years ago.
I have two interfaces, A and B. It happens that A includes B. Finally, I have a concrete implementation of A (call it Impl), which, by definition, also implements B.
For example:
type A interface {
Close() error
Read(b []byte) (int, error)
}
type Impl struct {}
func (I Impl) Read(b []byte) (int, error) {
fmt.Println("In read!")
return 10, nil
}
func (I Impl) Close() error {
fmt.Println("I am here!")
return nil
}
Since A requires Read(), and Impl implements A, it also satisfies io.Reader.
If I try to pass individual items across functions, it works fine. But if I try slices of A to functions expecting io.Reader, it fails.
Example:
func single(r io.Reader) {
fmt.Println("in single")
}
func slice(r []io.Reader) {
fmt.Println("in slice")
}
im := &Impl{}
// works
single(im)
// FAILS!
list := []A{t}
slice(list)
If I can pass an A to single(r io.Reader), why can I not pass []A to slice(r []io.Reader), and how would I correct it?
Actual implementation at https://play.golang.org/p/QOREQJTQhD just uncomment the last two lines in main() and the error shows:
main.go:38: cannot use list (type []A) as type []io.Reader in argument to slice
I kind of asked something similar here
In Go, how can I make a generic function with slices?
Sadly, this is definitely a weakness in Go. The only way for you to go around this is to make a new slice of type []io.Reader with elements from []A

Resources