Variadic generic arguments in Go [duplicate] - go

This question already has answers here:
Generic variadic argument in Go?
(3 answers)
Closed 8 months ago.
Let's say I want to make the equivalent of the JavaScript Array.splice function in Go, for Slices. I have the following code:
func splice(slice []int, index, amount int, elements ...int) []int {
newslice := make([]int, 0)
for i := 0; i < index; i++ {
newslice = append(newslice, slice[i])
}
for i := index + amount; i < len(slice); i++ {
newslice = append(newslice, slice[i])
}
for _, el := range elements {
newslice = append(newslice, el)
}
return newslice
}
This example will work, but only for arguments of type int. I want to make it generic, and I know that I should give the variadic argument elements the type interface{}, but how do I create a new slice with the type of that interface from inside the function?
In other words, how can I specify the type of the slice dynamically depending on the type of the arguments in the first line of the function, where newslice is created?

Using reflection
If you really want to do generic stuff, reflection is the ultimate answer.
See the MakeSlice documentation
in the reflection package for details on your problem.
You just need to retrieve the type of the incoming slice (using TypeOf(...))
and applying MakeSlice correctly.
Example of using reflection to create a slice:
y := []int{1,2,3}
t := reflect.TypeOf(y)
slice := reflect.MakeSlice(t, 0, 10)
slice = reflect.Append(slice, reflect.ValueOf(2))
fmt.Println(slice.Interface())
Run it here.
Using []interface{}
Another way to work with, is []interface{}, which can store any value
but may lead to runtime panics as you omit compiler type checking completely
(this is a bad thing).
Here is an example for using []interface{}
as storage for arbitrary values. With this you don't need to know the type in
your splice implementation, you just splice and use []interface{} for new slices.
This method has the drawback, that you can't convert some slice to []interface{} easily. You have to copy it manually, as described in posts before.
Conclusion
Regardless of which version you use, you will never get back type safety without
knowing the type and converting it back manually. There's no such thing in Go
which will do that for you. That means, that you'll have something like this
in your code to regain type safety:
x := []int{1,2,3,4}
y := splice(x, ...)
yn := []int(y)

Instead of emulating JavaScript in Go (why ???) I would like to suggest to compose simmilar required operations from the building blocks of SliceTricks.
They are:
Completely type agnostic (think "generics" for free).
Quite probably pretty faster compared to packing/unpacking whatsoever in/from a []interface{}.

Related

Unmarshalling in-place into a slice type in Go

Often when using go, not sure why, I get the urge to write something like
type data []event
especially when I know I'm going to be passing the slice around without thinking too much about its contents for much of the program. Sooner or later it's going to be time to unpack some data into that slice of events and I end up writing something like:
func (d *data)Unmarshal(b []byte){
//... lots of sad code that never works
}
No matter what I do I can never quite figure out how to bless my slice type with an unmarshal method that turns some bytes into the data type in-place.
When I give up, I either write a simpler function like func UnmarshalData(b []byte) data which feels like a retreat and makes it hard to write interfaces, or change the type in the first place and make a struct like
type data struct {
actuallyTheData []event
}
which feels like boilerplate purely to compensate for my lack of understanding.
So my question is: is it possible to write a function with a pointer receiver where the receiver is a slice type and that allows me to e.g. Unmarshal in-place?
The closest I can get, though it still doesn't work (and, let's face it, is pretty ugly), is something like:
type foo []int
func (f *foo) Unmarshal(s string) {
numbers := strings.Split(s, ",")
integers := make([]int, len(numbers))
for i, n := range numbers {
integer, err := strconv.Atoi(n)
if err != nil {
log.Fatal(err)
}
integers[i] = integer
}
my_f := foo(integers)
f = &my_f
}
Here's the full example: https://go.dev/play/p/3q7qehoW9tm. Why doesn't it work? What am I misunderstanding?
The last line in your Unmarshal function is overwriting the receiver itself, i.e. its address:
f = &my_f // changing the value of the pointer
The updated value won't be propagated to callers. From Declarations and Scope:
The scope of an identifier denoting a method receiver, function parameter, or result variable is the function body.
You must mutate the value that is being pointed to, then callers will see it upon dereference. (As a matter of fact, you don't have to convert to the defined slice type)
func (f *foo) Unmarshal(s string) {
// ...
integers := make([]int, len(numbers))
*f = integers
}
Fixed playground: https://go.dev/play/p/3JayxQMClt-

Function that returns Slice of Custom Interface

At first, I want to introduce you because I feel like I am missing some core Golang concept.
In my application, many models will have a method called GetByUserId. I created interface(UserCreatedEntity) that requires this method so then I will be able to create Controller's GetUserRecords method factory for each type of records with just:
router.Handle("/ideas/mine",
middlewares.AuthUser(controllers.GetMineFactory(&models.Idea{}))).Methods("POST")
router.Handle("/votes/mine",
middlewares.AuthUser(controllers.GetMineFactory(&models.Vote{}))).Methods("POST")
router.Handle("/someNewType/mine",
middlewares.AuthUser(controllers.GetMineFactory(&models.SomeNewType{}))).Methods("POST")
This is how my interface looks like:
type UserCreatedEntity interface {
GetByUserId(userId uint) []UserCreatedEntity
}
And implementation:
func (idea *Idea) GetByUserId(userId uint) []UserCreatedEntity {
ideas := []Idea{}
GetDB().
Table("ideas").
/** Query removed to make code less confusing **/
Scan(ideas)
return ideas
}
Obviously, it does not work (version with slice of pointers neither do). The thing is - This code would work if I return only one record - like that (obviously with changing signature in interface also):
func (idea *Idea) GetByUserId(userId uint) UserCreatedEntity {
idea := &Idea{}
GetDB().
Table("ideas").
/** Query removed to make code less confusing **/
First(idea)
return idea
}
How to make it work as slice? As I said I suspect that I am missing some important knowledge. So deep explaination would be awesome.
Solution:
func (idea *Idea) GetByUserId(userId uint) []UserCreatedEntity {
ideas := []*Idea{}
GetDB().
Table("ideas").
Select("problems.name AS problem_name, ideas.id, ideas.problem_id, ideas.action_description, ideas.results_description, ideas.money_price, ideas.time_price, ideas.is_published").
Joins("INNER JOIN problems ON ideas.problem_id = problems.id").
Where("ideas.user_id = ?", userId).
Scan(&ideas)
uces := make([]UserCreatedEntity, len(ideas))
for i, idea := range ideas {
uces[i] = idea
}
return uces
}
In programming language theory this is called variance, and it is not supported in Go. For much more details see this proposal.
Specifically, return types are not covariant. A slice of T does not implement a slice of I even if T implements I.
The FAQ entry linked above proposes this workaround:
It is necessary to copy the elements individually to the destination
slice. This example converts a slice of int to a slice of interface{}:
t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
s[i] = v
}
Though in your case the right solution may be different.
Interfaces are dynamic. Composite types that involve interfaces are not.
UserCreatedEntity is an interface, and Idea satisfies the interface, so you can return an Idea from a function whose signature has a return type of UserCreatedEntity.
[]UserCreatedEntity is a slice of UserCreatedEntity, not an interface. The only type that can be returned is []UserCreatedEntity. []Idea is a different type (slice of Idea). You can fill a []UserCreatedEntity with Idea elements, because each element is of type UserCreatedEntity, which again is an interface and Idea is allowed there.
Similarly, func() UserCreatedEntity is a type "function which returns UserCreatedEntity". You cannot subsitute a func() Idea because that is a different type. But you can return an Idea from a func() UserCreatedEntity because an Idea is a UserCreatedEntity.
If you weren't using Scan here, which presumably uses reflection, the fix would be to declare your local slice as []UserCreatedEntity instead of []Idea. Since you are using Scan, you instead must scan into a []Idea, then iterate over it to copy all the elements to a []UserCreatedEntity and return that.

"Cannot use variable of type []struct as []interface" while spreading [duplicate]

This question already has answers here:
slice of struct != slice of interface it implements?
(6 answers)
Closed 10 months ago.
Prototype function
func test(i ...interface{}) {
// Code here
}
Intended use
type foo struct {
// Fields
}
foos := []foo{
// foo1, foo2 ...
}
test(foos...) // ERROR
test(foos[1], foos[2], ...) // OK
Error
cannot use foos (variable of type []foos) as []interface{} value in argument to test
Description
The intended use is to be used like the built-in function append().
https://golang.google.cn/pkg/builtin/#append
func append(slice []Type, elems ...Type) []Type
Although, as I've seen append() doesn't use interface{}, which is strange, since anywhere that I searched all people say to use interface{} when you don't know the type. But they don't.
Nope, append() uses a "build-in" type called Type, which apparently the docs say that it's a int. Although, I cannot use it. There isn't such type. And neither I would know how to use it if there was.
https://golang.google.cn/pkg/builtin/#Type
type Type int
So, I'm very confused here.
Questions
Why does the spread operator not work as intended? For example, in Javascript the spread operator just spreads the array into items. But in Golang it seems like it keeps the same array parameter type as it is but gives the compiler later an instruction to spread it. Which is odd.
Are we even able to make similar custom mechanisms like append() at all? Or am I a dummy and I'm using something wrong anyway?
I think that this is the issue that you are running into here.
https://github.com/golang/go/wiki/InterfaceSlice
I am not an expert in this but have hit this before, the "slice of empty interface" is not an interface and therefore cannot be replaced by any type which is the issue that you are having, it has to do with the memory structure being different. The above has a far better explanation than one that I can give.
You can typecast your typed slice to get what you need:
generic := make([]interface{}, 0)
for _, f := range foos {
generic = append(generic, f)
}
test(generic...) // works
Combining the two non-negative answers into what I feel is the best solution:
interfaceSlice := make([]interface{}, len(foos))
for i, v := range foos {
interfaceSlice[i] = v
}
test(interfaceSlice...)
Partially inspired by:
https://github.com/golang/go/wiki/InterfaceSlice#what-can-i-do-instead

How to list pointers to all fields of golang struct?

Is there a good way in golang to pass all fields of some struct instance c?
I'm looking for some syntactic sugar functionality, so that instead of doing this:
method(&c.field1, &c.field2, &c.field3, &c.field4, &c.field5, ...)
I could do this:
method(FieldsPointers(c)...)
I'm rather new to golang and still learning the basics, if there is no good way to do what I want for a good reason, I'd appreciate an explanation as to why.
Besides all sql specified tools, if you want to access to pointers of a struct, you can use reflect. Be warned that the package is tricky and rob pike said it is not for everyone.
reflect.Value has methods NumField which returns the numbber of fields in the struct and Field(int) which accepts the index of a field and return the field itself.
But as you want to set a value to it, it is more complicated than just calling the two methods. Let me show you in code:
func Scan(x interface{}) {
v := reflect.ValueOf(x).Elem()
for i := 0; i < v.NumField(); i++ {
switch f := v.Field(i); f.Kind() {
case reflect.Int:
nv := 37
f.Set(reflect.ValueOf(nv))
case reflect.Bool:
nv := true
f.Set(reflect.ValueOf(nv))
}
}
}
First, you need to pass a pointer of the struct into Scan, since you are modifying data and the value must be settable. That is why we are calling .Elem(), to dereference the pointer.
Second, reflect.Value.Set must use a same type to set. You cannot set uint32 to a int64 like normal assignment.
Playground: https://play.golang.org/p/grvXAc1Px8g

How to iterate over different types in loop in Go?

In Go, in order to iterate over an array/slice, you would write something like this:
for _, v := range arr {
fmt.Println(v)
}
However, I want to iterate over array/slice which includes different types (int, float64, string, etc...). In Python, I can write it out as follows:
a, b, c = 1, "str", 3.14
for i in [a, b, c]:
print(i)
How can I do such a work in Go? As far as I know, both array and slice are supposed to allow only same-type object, right? (say, []int allows only int type object.)
Thanks.
As Go is a statically typed language, that won't be as easy as in Python. You will have to resort to type assertions, reflection or similar means.
Take a look at this example:
package main
import (
"fmt"
)
func main() {
slice := make([]interface{}, 3)
slice[0] = 1
slice[1] = "hello"
slice[2] = true
for _, v := range slice {
switch v.(type) {
case string:
fmt.Println("We have a string")
case int:
fmt.Println("That's an integer!")
// You still need a type assertion, as v is of type interface{}
fmt.Printf("Its value is actually %d\n", v.(int))
default:
fmt.Println("It's some other type")
}
}
}
Here we construct a slice with the type of an empty interface (any type implements it), do a type switch and handle the value based on the result of that.
Unfortunately, you'll need this (or a similar method) anywhere where you'll be dealing with arrays of unspecified type (empty interface). Moreover, you'll probably need a case for every possible type, unless you have a way to deal with any object you could get.
One way would be to make all of the types you want to store implement some interface of yours and then only use those objects through that interface. That's kind of how fmt handles generic arguments – it simply calls String() on any object to get its string representation.

Resources