Экспорт функций с анонимной структурой в качестве параметра [нельзя использовать значение (тип struct {…}) в качестве типа struct {…} в качестве аргумента для package.Func]

Вот кусок кодаplay.google.org который работает без проблем:

package main

import (
    "fmt"
)

func PrintAnonymous(v struct {
    i int
    s string
}) {
    fmt.Printf("%d: %s\n", v.i, v.s)
}

func PrintAnonymous2(v struct{}) {
    fmt.Println("Whatever")
}

func main() {
    value := struct {
        i int
        s string
    }{
        0, "Hello, world!",
    }
    PrintAnonymous(value)
    PrintAnonymous2(struct{}{})
}

Однако еслиPrintAnonymous() Функция существует в другом пакете (скажем,temp), код не будет работать:

cannot use value (type struct { i int; s string })
as type struct { i int; s string } in argument to temp.PrintAnonymous

Мой вопрос:

Есть ли способ вызвать (публичную) функцию с анонимной структурой в качестве параметра (a.k.a.PrintAnonymous() выше)?Функция с пустой структурой в качестве параметра (a.k.a.PrintAnonymous2() выше) можно вызвать, даже если он существует в другом пакете. Это особый случай?

Ну, я знаю, что всегда могу назватьstruct Чтобы решить проблему, мне просто любопытно по этому поводу, и мне интересно, почему кажется, что это не разрешено.

 Dale05 авг. 2016 г., 10:54
Что произойдет, если вы в верхнем регистре свойства структуры? struct {I int S string}
 Siu Ching Pong -Asuka Kenji-05 авг. 2016 г., 10:56
@ Дейл: Да! Я тоже это заметил! Спасибо! Как я глупа!

Ответы на вопрос(2)

что имена полей в нижнем регистре, и, следовательно, не являются публичными! Изменение первой буквы имени поля в верхний регистр решает проблему:

package main

import (
    "temp"
)

func main() {
    value := struct {
        I int
        S string
    }{
        0, "Hello, world!",
    }
    temp.PrintAnonymous(value)
    temp.PrintAnonymous2(struct{}{})
}
package temp

import (
    "fmt"
)

func PrintAnonymous(v struct{I int; S string}) {
    fmt.Printf("%d: %s\n", v.I, v.S)
}

func PrintAnonymous2(v struct{}) {
    fmt.Println("Whatever")
}
Решение Вопроса

что вы не можете создавать значения этой структуры и указывать значения для полей из другого пакета.Spec: составные литералы:

Ошибочно указывать элемент для неэкспортированного поля структуры, принадлежащей другому пакету.

Если вы измените определение структуры для экспорта полей, то оно будет работать, потому что все поля могут быть назначены другими пакетами (см. Ответ Сиу Чинг Понг - Асуки Кэндзи).

Это также относится и к пустой структуре (без полей): пустая структура не имеет полей, поэтому у нее нет неэкспортированных полей, поэтому вы можете передать значение этого параметра.

Вы можете вызвать функцию с неизмененной структурой (с неэкспортированными полями) черезотражение хоть. Вы можете получитьreflect.Type изPrintAnonymous() функция, и вы можете использоватьType.In() чтобы получитьreflect.Type своего первого параметра. Это анонимная структура, для которой мы хотим передать значение. И вы можете построить значение этого типа, используяreflect.New(), Это будетreflect.Value, оборачивая указатель нанулевое значение анонимной структуры. К сожалению, вы не можете иметь структурное значение с полями, имеющими ненулевые значения (по причине, указанной выше).

Вот как это может выглядеть:

v := reflect.ValueOf(somepackage.PrintAnonymous)
paramt := v.Type().In(0)
v.Call([]reflect.Value{reflect.New(paramt).Elem()})

Это напечатает:

0: 

0 нулевое значение дляint, а также"" пустая строка дляstring.

Для более глубокого понимания системы типов и структур с неэкспортированными полями см. Связанные вопросы:

Идентифицируйте не встроенные типы, используя отражение
Как клонировать структуру с неэкспортированным полем?

Что интересно (это ошибкасм. связанную проблему ниже), используя рефлексию, вы можете использовать значение вашего собственного анонимного типа структуры (с совпадающими, неэкспортированными полями), и в этом случае мы можем использовать значения, отличные от нулевого значения полей структуры:

value := struct {
    i int
    s string
}{
    1, "Hello, world!",
}

v.Call([]reflect.Value{reflect.ValueOf(value)})

Выше бегает (без паники):

1: Hello, world!

Причина, по которой это разрешено, связана с ошибкой в ​​компиляторе. Смотрите пример кода ниже:

s := struct{ i int }{2}

t := reflect.TypeOf(s)
fmt.Printf("Name: %q, PkgPath: %q\n", t.Name(), t.PkgPath())
fmt.Printf("Name: %q, PkgPath: %q\n", t.Field(0).Name, t.Field(0).PkgPath)

t2 := reflect.TypeOf(subplay.PrintAnonymous).In(0)
fmt.Printf("Name: %q, PkgPath: %q\n", t2.Name(), t2.PkgPath())
fmt.Printf("Name: %q, PkgPath: %q\n", t2.Field(0).Name, t2.Field(0).PkgPath)

Выход:

Name: "", PkgPath: ""
Name: "i", PkgPath: "main"
Name: "", PkgPath: ""
Name: "i", PkgPath: "main"

Как вы можете увидеть неэкспортированное полеi в обоих типах анонимных структур (вmain пакет и вsomepackage в качестве параметра дляPrintAnonymous() function) –falsely– сообщать об одном и том же пакете, и, следовательно, их тип будет равен:

fmt.Println(t == t2) // Prints true

Примечание: я считаю этоизъян: если это разрешено с помощью отражения, то это должно быть возможно без использования отражения тоже. Если без отражения ошибка времени компиляции оправдана, то использование отражения должно привести к панике во время выполнения. Я открыл для этого вопрос, вы можете следить за ним здесь:проблема № 16616, Исправить на данный момент цели Go 1.8.

 Siu Ching Pong -Asuka Kenji-05 авг. 2016 г., 13:25
Хорошая информация! Спасибо!

Ваш ответ на вопрос