Как вынуть из пустого интерфейса значение с зарание неизвестным типом?

146 views
Skip to first unread message

Александр Рем

unread,
Jun 18, 2019, 1:24:42 PM6/18/19
to Golang Russian
Мучаюсь с этим вопросом и как-то всё не то...

Имеем:
func Fu(instance interface{}) interface{} {
   //Неким образом извлекаем значение обёрнутое в интерфейс или получаем его тип
   //Создаём экземпляр (если это был тип)
   //Работа с объектом
    return result
}


но, не пойму
reflect.TypeOf() - не годится для new()
а
reflect.ValueOf() - это type reflect.Value, что не годится для работы

Daniel Podolsky

unread,
Jun 18, 2019, 3:05:34 PM6/18/19
to gola...@googlegroups.com
type switch по всем возможным вариантам. ну или никак...
> --
> Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
> Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
> Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/golang-ru/fc5570d5-7062-409a-b011-c463fa28d21b%40googlegroups.com.
> Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.

Alex Lurye

unread,
Jun 18, 2019, 3:27:49 PM6/18/19
to Golang Russian
tp := reflect.TypeOf(instance)
newInstanceValue := reflect.New(tp)
newInstance := newInstanceValue.Interface()

Не очень понятно, что именно вы понимаете под "Работа с объектом". Переменная newInterface имеет тип interface{}, и с ним или generic-операции можно делать, или к конкретному типу преобразовывать.

А что за задачу вы решаете? Рефлексия в жизни нужна в очень редких случаях типа парсеров, сериализаторов, драйверов баз данных и прочих штук, которые заранее не знают, с каким типами им работать.


--

Александр Рем

unread,
Jun 18, 2019, 11:19:52 PM6/18/19
to Golang Russian
Спасибо попробую.


Рефлексия в жизни нужна в очень редких случаях типа парсеров, сериализаторов, драйверов баз данных и прочих штук, которые заранее не знают, с каким типами им работать.

;) Ну, вот ведь.., и не так уж редко получилось.

Элементарная ситуация:

func MaterializeFromFile(instance  interface{}, path string) ( interface{}, error)  {
    file
, err := os.Open(path)
   
if err != nil {
       
return nil , err
   
} else {
    restore
:= // А вот здесь нужно понять, какого типа переменная, тип берётся из instance, т.к. ниже decoder.Decode() принимает значение определённого типа
// вероятно, можно передавать и просто сразу тип, но казалось, что с готовым объектом(хз. как ещё назвать всё в Go одним термином) будет реализовать проще...(но не тут-то было)
    decoder
:= gob.NewDecoder(file)
    decoder
.Decode(&restore)
   
return restore, nil }
}



вторник, 18 июня 2019 г., 22:27:49 UTC+3 пользователь Alex Lurye написал:
tp := reflect.TypeOf(instance)
newInstanceValue := reflect.New(tp)
newInstance := newInstanceValue.Interface()

Не очень понятно, что именно вы понимаете под "Работа с объектом". Переменная newInterface имеет тип interface{}, и с ним или generic-операции можно делать, или к конкретному типу преобразовывать.

А что за задачу вы решаете? Рефлексия в жизни нужна в очень редких случаях типа парсеров, сериализаторов, драйверов баз данных и прочих штук, которые заранее не знают, с каким типами им работать.


On Tue, Jun 18, 2019 at 7:24 PM Александр Рем <gen...@live.ru> wrote:
Мучаюсь с этим вопросом и как-то всё не то...

Имеем:
func Fu(instance interface{}) interface{} {
   //Неким образом извлекаем значение обёрнутое в интерфейс или получаем его тип
   //Создаём экземпляр (если это был тип)
   //Работа с объектом
    return result
}


но, не пойму
reflect.TypeOf() - не годится для new()
а
reflect.ValueOf() - это type reflect.Value, что не годится для работы

--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес gola...@googlegroups.com.

Александр Рем

unread,
Jun 19, 2019, 12:15:44 AM6/19/19
to Golang Russian
Ммм... Печаль...
Так не работает. Тип-то newInstance действительно по прежнему interface {}

Err: gob: local interface type *interface {} can only be decoded from remote interface type; received concrete type ExmplStrct = struct { ID string; X string; Y int; Z int; }

Как-то не верится, что нельзя создать объект опосредовано, через рефлексию...
Но может тогда можно в функцию передовать непосредственно значение типа? (в виде некого самостоятельного значения или в виде строки)

вторник, 18 июня 2019 г., 22:27:49 UTC+3 пользователь Alex Lurye написал:
tp := reflect.TypeOf(instance)

Alex Lurye

unread,
Jun 19, 2019, 12:50:49 AM6/19/19
to Golang Russian
1. Покажите код - вы что-то не так делаете, скорее всего. Вы уверены, что передаёте указатель на созданный объект, а не указатель на interface {}?

2. В вашем случае рефлексия-таки не нужна. Поменяйте API вашей функции на такое, где пользователь сам передает указатель на структуру, которую надо десериализовать (пусть даже и interface{}), и вы этот указатель просто один в один передаёте дальше в Decode.


--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/golang-ru/fc6265a2-769f-48de-95ca-5663e796f0e3%40googlegroups.com.

Александр Рем

unread,
Jun 19, 2019, 2:57:11 AM6/19/19
to Golang Russian
Ну код он вот:
ничего хитрого

func MaterializeFromFile(instance  interface{}, path string) ( interface{}, error)  {
    file
, err := os.Open(path)
   
if err != nil {
       
return nil , err
   
} else {


    restore
:= /* А вот здесь нужно понять, какого типа переменная, тип берётся из instance, т.к. ниже decoder.Decode() принимает значение определённого типа.
Вероятно, можно передавать и просто сразу тип, но казалось, что с
готовым объектом(хз. как ещё назвать всё в Go одним термином) будет
реализовать проще...(но не тут-то было) */

    decoder
:= gob.NewDecoder(file)
    decoder
.Decode(&restore)
   
return restore, nil }
}

В чём собствено смысл?
В том, что я хочу часть кода по сериализации/десериализации выделить в отдельный компонент и "никогда" больше не возвращаться к нему. Сл. нужно максимальное абстрагирование, ибо сегодня я использую одни структуры.., завтра захочу другие, послезавтра передам часть работ напарнику и не хочу заморачиваться объяснениями как мне надо что бы он сериализовал его структуры (ибо в целом над этими двумя простыми функциями может быть ещё довольно сложная логика (например вычесление/создание путей, и т.д.))

Тут пустой интерфейс вписывается хорошо.
Ведь если я сделаю "такое, где пользователь сам передает указатель на структуру, которую надо десериализовать", то как я укажу этот тип (неизвестный зарание) в аргументах функции? Всё-равно же через какой-то интерфейс?
Быть может передавать на вход тип? Но по этому поводу я вообще ненашёл информации

Выше этого компонента прикладной компонент уже сам знает с какими типами он работает и сл. может спокойно привести пустой интерфейс (который отдаёт MaterializeFromFile() ) к нужному типу при помощи switch

среда, 19 июня 2019 г., 7:50:49 UTC+3 пользователь Alex Lurye написал:
1. Покажите код - вы что-то не так делаете, скорее всего. Вы уверены, что передаёте указатель на созданный объект, а не указатель на interface {}?

2. В вашем случае рефлексия-таки не нужна. Поменяйте API вашей функции на такое, где пользователь сам передает указатель на структуру, которую надо десериализовать (пусть даже и interface{}), и вы этот указатель просто один в один передаёте дальше в Decode.


On Wed, Jun 19, 2019, 06:15 Александр Рем <gen...@live.ru> wrote:
Ммм... Печаль...
Так не работает. Тип-то newInstance действительно по прежнему interface {}

Err: gob: local interface type *interface {} can only be decoded from remote interface type; received concrete type ExmplStrct = struct { ID string; X string; Y int; Z int; }

Как-то не верится, что нельзя создать объект опосредовано, через рефлексию...
Но может тогда можно в функцию передовать непосредственно значение типа? (в виде некого самостоятельного значения или в виде строки)

вторник, 18 июня 2019 г., 22:27:49 UTC+3 пользователь Alex Lurye написал:
tp := reflect.TypeOf(instance)
newInstanceValue := reflect.New(tp)
newInstance := newInstanceValue.Interface()


--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес gola...@googlegroups.com.

Andrey Velikoredchanin

unread,
Jun 19, 2019, 3:01:11 AM6/19/19
to golang-ru
Может вам передавать map[string]interface{} и потом уже его в коде разбирать?

ср, 19 июн. 2019 г. в 09:57, Александр Рем <gen...@live.ru>:
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/golang-ru/ce8101ca-8c9c-4e1f-a3ae-b399556be3c1%40googlegroups.com.

Александр Рем

unread,
Jun 19, 2019, 3:04:24 AM6/19/19
to Golang Russian
UPD

И ведь главное ЧСХ gob то знает какая структура лежит в gob-файле ;)
судя по ошибке: "gob: local interface type *interface {} can only be decoded from remote interface type; received concrete type ExmplStrct = struct { ID string; X string; Y int; Z int; }"
но кривляется и требует что бы я явно указал её в .Decode()

по идее бы как раз .Decode() должен принимать пустой интерфейс...

Degtyarev Evgeny

unread,
Jun 19, 2019, 3:14:23 AM6/19/19
to Golang Russian
Автор, покажи как вызываешь MaterializeFromFile??

Александр Рем

unread,
Jun 19, 2019, 3:17:28 AM6/19/19
to Golang Russian
map[string]interface{} - видел такую конструкцию несколько раз, но не понял её "физического смысла", попробую теперь вникнуть

среда, 19 июня 2019 г., 10:01:11 UTC+3 пользователь Andrey Velikoredchanin написал:

Александр Рем

unread,
Jun 19, 2019, 3:26:33 AM6/19/19
to Golang Russian
Не очень понял вопрос. Что значит как вызываешь?

Сейчас функция имеет вид
func MaterializeFromFile(instance  interface{}, path string) ( interface{}, error)  {...}
Соответственно так и вызываю.

Любой компонент который хочет десериализовать нечто из gob-файла и знает тип и путь, может воспользоваться этой функцией и получить искомое в пустом интерфейсе, после чего привести к нужному типу (ибо тип он знает)
upd
instance может быть как экземпляром нужного типа, так и собственно типом (типа тип или стринг (просто как так реализовать я совсем не понял))


среда, 19 июня 2019 г., 10:14:23 UTC+3 пользователь Degtyarev Evgeny написал:

Alex Lurye

unread,
Jun 19, 2019, 3:28:01 AM6/19/19
to Golang Russian
Вы самую важную часть и не привели. "..." в строке "restore := ..." скорее всего и содержит баг.

Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/golang-ru/ce8101ca-8c9c-4e1f-a3ae-b399556be3c1%40googlegroups.com.

Александр Рем

unread,
Jun 19, 2019, 3:34:08 AM6/19/19
to Golang Russian


среда, 19 июня 2019 г., 10:28:01 UTC+3 пользователь Alex Lurye написал:
Вы самую важную часть и не привели. "..." в строке "restore := ..." скорее всего и содержит баг.


ммм... видимо Вы об этом.
Это код который я попробовал основываясь на Вашей подсказке, может и действительно ещё какой то баг допустил, ибо чисто с обезьяничал не разбираясь...
func MaterializeFromFile(instance  interface{}, path string) ( interface{}, error)  {
    file
, err := os.Open(path)
   
if err != nil {
       
return nil , err
   
} else {

        tp
:= reflect.TypeOf(instance)
        newInstanceValue
:= reflect.New(tp)
        newInstance
:= newInstanceValue.Interface()
       
       
        decoder
:= gob.NewDecoder(file)        
        dec
оdeErr := decoder.Decode(&newInstance)
       
if decоdeErr != nil {
            fmt
.Println( "Ошибко", decоdeErr)
       
}
       
return newInstance, nil }
}



Alex Lurye

unread,
Jun 19, 2019, 3:45:35 AM6/19/19
to Golang Russian
У вас, похоже, лишний &newInstance там, где нужно просто newInstance:
Должно быть просто: decoder.Decode(newInstance).

При всём этом, я утверждаю, что reflect в этой задаче не нужен от слова совсем. Ваша функция может быть такой:

func MaterializeFromFile(instance interface{}, path string) error {
  file, err := os.Open(path)
  if err != nil {
    return err
  }
  defer file.Close() // Не забывайте закрывать файлы.
  return gob.NewDecoder(file).Decode(instance)
}

Никакой рефлексии, подходит для чтения любых типов, а конкретный тип определяется пользователем в момент вызова:

var data ExmplStruct
if err := MaterializeFromFile(&data, "/my/file"); err != nil {
   ...
}
// Тут data заполнена считанным значением.

--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/golang-ru/837fad7f-fd56-4fa4-9087-a2c929842cb8%40googlegroups.com.

Александр Рем

unread,
Jun 19, 2019, 4:01:30 AM6/19/19
to Golang Russian
Да так работает и гораздо изящнее.
Я не догадался запихнуть в пустой интерфейс сразу ссылку.

Спасибо.

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


среда, 19 июня 2019 г., 10:45:35 UTC+3 пользователь Alex Lurye написал:
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес gola...@googlegroups.com.

Andrey Velikoredchanin

unread,
Jun 19, 2019, 4:08:17 AM6/19/19
to golang-ru

ср, 19 июн. 2019 г. в 11:01, Александр Рем <gen...@live.ru>:
Я правда не совсем понял, что там за магия и почему в случае ссылки интерфейс как бы "прозрачен", а в случаее самого значения нет, но это уже выходит за рамки обсуждения, авось усвоится со временем.

Тут все просто. Методы типа обычно делаются на ссылку. Т.к. это позволяет менять данные в оригинальном инстансе. Соответственно и интерфейсу соответствует обычно не сам инстанс, а ссылка на него. Т.к. методы у ссылки.

Надеюсь, понятно объяснил.


Daniel Podolsky

unread,
Jun 19, 2019, 4:13:59 AM6/19/19
to gola...@googlegroups.com
так а не надо такую ситуацию создавать.

go - язык статической типизации, не надо в него тянуть динамику
> Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
> Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/golang-ru/51ec62af-69fe-43d0-9a98-fb2ea6adcae1%40googlegroups.com.

Daniel Podolsky

unread,
Jun 19, 2019, 4:14:53 AM6/19/19
to gola...@googlegroups.com
> В том, что я хочу часть кода по сериализации/десериализации выделить в отдельный компонент и "никогда" больше не возвращаться к нему.

кодогенерация ваш друг
> Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
> Чтобы посмотреть обсуждение на веб-странице, перейдите по ссылке https://groups.google.com/d/msgid/golang-ru/ce8101ca-8c9c-4e1f-a3ae-b399556be3c1%40googlegroups.com.

Alex Lurye

unread,
Jun 19, 2019, 4:15:40 AM6/19/19
to Golang Russian
On Wed, Jun 19, 2019 at 10:13 AM Daniel Podolsky <onok...@gmail.com> wrote:
так а не надо такую ситуацию создавать.

go - язык статической типизации, не надо в него тянуть динамику

Вот да! Эту строчку надо написать жирным капсом на главной странице языка.
Случается, динамическая типизация нужна, но в ооооочень редких случаях, как если бы вы свой GOB писали.
 
Просмотреть это обсуждение в Сети можно по адресу https://groups.google.com/d/msgid/golang-ru/CAKaHDOEPu3vtO7D3CRm__HefDXPe-f9%2BSkJ8S5%2B3GhxgbaB9QQ%40mail.gmail.com.
Настройки подписки и доставки писем: https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages