Golang: как передать в функцию тип и вернуть указатель на созданный массив объектов этого типа?

Собственно вопрос в заголовке.

Нужно подобие фабрики, которая будет создавать массив вычисляемого внутри фабрики размера и возвращать его наружу. Как можно передать такой фабрике "тип" создаваемых структур в массиве?

Что-то типа такого:

func myMalloc(someType string) []interface{} {
    count := MyCalCount(db, "date = '2020-01-01'")
    return make([]someType, count)
}

То есть количество элементов вычисляется сторонней функцией, тип объектов в массиве известен, но их несколько (и достаточно много .. около 500) .. Можно ли создать подобную фабрику?


Ответы (3 шт):

Автор решения: prospero78su
  1. Настоятельно не рекомендую работать с массивами. Если только вы уверены на 146%, что вам не потребуется добавлять или удалять элементы такого массива
  2. Никогда не используйте interface{} в качестве входных и/или выходных параметров. Всегда определяйте производный пользовательский тип на основе interface{}
  3. Всегда проверяйте результаты своих операций. Возможно, один из передаваемых параметров может оказаться недопустимым.
  4. Если вам нужны несколько типов возвращаемых значений, а вызов конкретной функции может быть не определён -- передавайте в качестве значений интерфейсы:
type IArray interface {
   Len() int
}

type IFabricCalcArray interface {
  Make(size int) (IArray, error)
}
→ Ссылка
Автор решения: Ainar-G

Во-первых, у вас большая путаница с терминологией. Массивы в го всегда имеют константный размер и обозначаются [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()
}

Ссылка на полный пример.

→ Ссылка
Автор решения: Arhat109
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 ошибок не видит, но не проверял. :)

Вот .. что-то типа этого хочется. :)

→ Ссылка