I am populating lists of structs from a postgres database using the native sql library and jmoiron/sqlx. I currently have separate functions for different types, for example:
func selectAccounts(ext sqlx.Ext, query string, args []interface{}) ([]Account, error) {
var accts []Account
rows, err := ext.Queryx(query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var a Account
if err = rows.StructScan(&a); err != nil {
return nil, err
}
accts = append(accts, a)
}
err = rows.Err()
return accts, err
}
... but I have a requirement to build a generic function that accepts interface{} and returns a []interface{}. I am finding the process of replacing the line var a Account
above is difficult, as it requires making a copies of the passed in interface{} type while replicating its underlying type. Should I resign myself to the fact that I need to be using reflection, or is there another way to go about doing this?
Couldn't you make your structs polymorphic and have them implement a single interface? Then your function can return a slice of said interface? This way you wouldn't have to use reflection. I was thinking something along these lines:
package main
import (
"fmt"
)
type Account interface {
GetDetails() string
}
type PersonAccount struct {
Name string
}
func NewPersonAccount(name string) Account {
return &PersonAccount{
Name: name,
}
}
func (account *PersonAccount) GetDetails() string {
return account.Name
}
type BillingAccount struct {
AccountNumber string
}
func NewBillingAccount(accountNumber string) Account {
return &BillingAccount{
AccountNumber: accountNumber,
}
}
func (account *BillingAccount) GetDetails() string {
return account.AccountNumber
}
func getAllAccounts() []Account {
accounts := make([]Account, 0)
accounts = append(accounts, NewPersonAccount("John Doe"))
accounts = append(accounts, NewBillingAccount("1234-5678"))
return accounts
}
func main() {
accounts := getAllAccounts()
for _, account := range accounts {
fmt.Println(account.GetDetails())
}
}
/* Output:
John Doe
1234-5678
*/
First, there seems to be a difference between what you mention in your description as of now
but I have a requirement to build a generic function that accepts interface{} and returns a []interface{}.
and what your code does (it accepts a slice of an interface and not an interface).
You may not need to accept a slice of interfaces in the function parameters as your code currently does, can just accept an interface, so your function can look like :
func selectAccounts(ext sqlx.Ext, query string, args interface{}) ([]Account, error) {
You can return an slice of interface and have any type inside of it. Please take a look at this https://play.golang.org/p/NcLxhsiqK8
As @ivan.sim mentions, you can just append Account
to the interface slice instead of creating the slice Accounts
.