Golang: как передать в функцию тип и вернуть указатель на созданный массив объектов этого типа?
Собственно вопрос в заголовке.
Нужно подобие фабрики, которая будет создавать массив вычисляемого внутри фабрики размера и возвращать его наружу. Как можно передать такой фабрике "тип" создаваемых структур в массиве?
Что-то типа такого:
func myMalloc(someType string) []interface{} {
count := MyCalCount(db, "date = '2020-01-01'")
return make([]someType, count)
}
То есть количество элементов вычисляется сторонней функцией, тип объектов в массиве известен, но их несколько (и достаточно много .. около 500) .. Можно ли создать подобную фабрику?
Ответы (3 шт):
- Настоятельно не рекомендую работать с массивами. Если только вы уверены на 146%, что вам не потребуется добавлять или удалять элементы такого массива
- Никогда не используйте
interface{}в качестве входных и/или выходных параметров. Всегда определяйте производный пользовательский тип на основеinterface{} - Всегда проверяйте результаты своих операций. Возможно, один из передаваемых параметров может оказаться недопустимым.
- Если вам нужны несколько типов возвращаемых значений, а вызов конкретной функции может быть не определён -- передавайте в качестве значений интерфейсы:
type IArray interface {
Len() int
}
type IFabricCalcArray interface {
Make(size int) (IArray, error)
}
Во-первых, у вас большая путаница
с терминологией. Массивы в го всегда имеют константный размер
и обозначаются [N]T. make же используется
со срезами, обозначаемыми []T.
Во-вторых, тип из строки вы не получите, это
не руби. Тип вы можете получить только передав в функцию
экземпляр этого типа. Разве что, если у вас будет заготовленная
хеш-таблица reflect.Type'ов.
В-третьих, то, чего вы хотите, это как-то совершенно
не по-гоферски. Если вам нужно сделать срез, так и используте
просто make. Подобные динамические измывательства над
типами — зло. Вы недостаточно подробно описали
архитектурную задачу, которую пытаетесь решить, но ей в любом случае есть
более подходящее решение.
Если после всего этого вы всё же хотите продолжить, вот вариант
через reflect. Не говорите, что вас
не предупреждали.
func f(v interface{}) (sl interface{}) {
typ := reflect.TypeOf(v)
if typ == nil {
return nil
}
styp := reflect.SliceOf(typ)
return reflect.MakeSlice(styp, 10, 10).Interface()
}
Ссылка на полный пример.
package sqlbuilder
import (
"database/sql"
"fmt"
"strings"
)
type QPart map[string]interface{}
type ActiveRecord interface{
NewAR() (interface{}, []interface{})
}
func FetchAll(
forObj ActiveRecord, db *sql.DB, table string, opts QPart, addons QPart,
)(
[]uint64, map[uint64]*interface{}, error,
){
var(
countRows uint32
strSql string
args []interface{}
rows *sql.Rows
err error
pkey int
)
if strSql,args,err = GetSql(table, opts, addons); err != nil { return nil, nil, err }
if countRows,err = FetchCount(db, strSql, args); err != nil { return nil, nil, err }
if rows,err = db.Query(strSql, args); err != nil { return nil, nil, err }
defer func(){ err = rows.Close() }()
pkey = -1
if pkeys,ok := addons["indexedBy"]; ok {
pkey = pkeys.(int)
}
var i uint64 = 0
ids := make([]uint64, countRows)
ret := make( map[uint64]*interface{}, countRows)
for rows.Next() {
item, fields := forObj.NewAR()
if err = rows.Scan( fields ); err != nil { return nil, nil, err }
index,ok := fields[pkey].(uint64)
if pkey < 0 || !ok { index = i }
ret[ index ] = &item
ids[i] = index
i++
}
return ids, ret, nil
}
//=============== реализация =============== //
type TblUsers struct {
ID uint64
Name string
IsActive bool
}
func (t *TblUsers) String() string { return t.Name }
func (t *TblUsers) NewAR() (interface{}, []interface{}) {
newItem := new(TblUsers)
fields := []interface{}{ &newItem.ID, &newItem.Name, &newItem.IsActive }
return newItem, fields
}
// получение выборки по условиям
func (t *TblUsers) getData() {
pdo, _ := sql.Open("mysql", "user:password@/schema")
ids, data, _ := FetchAll(t, pdo, "users",
QPart{ "is_active" : "1", "indexedBy" : 0 },
QPart{ "limit" : []string{"10","100"} },
)
fmt.Printf("\nIDS:\n%#v\nDATA:\n%#v\n", ids, data)
return
}
Без привлечение reflection пока получилось как-то так .. Goland ошибок не видит, но не проверял. :)
Вот .. что-то типа этого хочется. :)