导出带有匿名结构的函数作为参数[在package.Func的参数中不能使用value(type struct {...})作为类型struct {...} [英] Exporting functions with anonymous struct as a parameter [cannot use value (type struct {...}) as type struct {...} in argument to package.Func]

查看:669
本文介绍了导出带有匿名结构的函数作为参数[在package.Func的参数中不能使用value(type struct {...})作为类型struct {...}的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是一段代码 play.google.org 运行时没有任何问题:

  package main 

import
$ f


func PrintAnonymous (%d:%s \ n,vi,vs)
}

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


func main(){
value:= struct {
i int
s string
} {
0, Hello,world!,
}
PrintAnonymous(value)
PrintAnonymous2(struct {} {})
}
PrintAnonymous()函数(比如说,>
$ b>

 不能使用值(type struct {int; s string})
作为str类型uct {i int;我的问题是:



$ b $ p

$ p







$

  • 有没有办法用匿名结构作为参数调用(public)函数(又名 PrintAnonymous()> )?

  • 可以调用一个空结构作为参数的函数(又名 PrintAnonymous2()),即使它存在于另一个函数中包。这是一个特例吗?



好的,我知道我总是可以命名 struct 来解决这个问题,我只是好奇这一点,并想知道为什么它看起来不允许这样做。

解决方案

匿名结构类型的字段是未导出的。这意味着你不能创建这个结构的值,并为另一个包中的字段指定值。 规格:复合文字:


为属于不同包的结构的非导出字段指定元素是错误的。


如果您更改结构定义以导出字段,那么它将起作用,因为所有字段都可以由其他程序包分配(请参阅Siu Ching Pong -Asuka Kenji的回答)。



空结构也是这样(没有字段):空结构没有字段,因此它没有未导出的字段,所以你可以传递一个值。 p>

您可以通过反思虽然。您可以获取 reflect.Type PrintAnonymous()函数,您可以使用 Type.In()来获取 reflect.Type 的第一个参数。这是我们想要传递值的匿名结构。你可以使用 reflect.New()来构建该类型的值。 code> 。这将是 reflect.Value ,包装一个指向匿名结构的零值的指针。对不起,你不能有一个结构值与非零值的字段(由于上述原因)。



这是如何看起来像:

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

这将打印:

  0:
int ,c>

0 c>和 string 的空字符串。

对于类型系统内部的更深层次和未导出域的结构,请参阅相关问题:



如何克隆一个带有未导出域的结构?




有趣的是( ,请参阅下面的链接问题),使用反射,您可以使用您自己的匿名结构类型的值(具有匹配的未导出的字段),在这种情况下,我们可以使用结构字段的零值之外的值:

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

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

上面的运行(没有恐慌):

  1:你好,世界! 

允许这样做的原因是由于编译器中的错误。请参阅下面的示例代码:

  s:= struct {i int} {2} 

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

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

输出为:

 名称:,PkgPath:
名称:i,PkgPath:main
名称:,PkgPath:
名称:i,PkgPath:main
i
c $ c>> main
包和 somepackage 作为 PrintAnonymous()
函数的参数) -falsely-报告相同的包裹,因此他们的类型将是平等的:



< pre class =lang-golang prettyprint-override> fmt.Println(t == t2)//打印真实

注意:我认为这是一个缺陷:如果允许使用反射,则应该可以不使用反射。如果没有反映,编译时错误是合理的,那么使用反射应该导致运行时恐慌。我为此打开了一个问题,你可以在这里关注它: issue#16616 。修正目前的目标是1.8。


Here is a piece of code play.google.org that runs without any problem:

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{}{})
}

However, if the PrintAnonymous() function exists in another package (let's say, temp), the code will not work:

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

My question are:

  • Is there a way to call a (public) function with anonymous struct as a parameter (a.k.a. PrintAnonymous() above)?
  • A function with empty struct as a parameter (a.k.a. PrintAnonymous2() above) can be called even if it exists in another package. Is this a special case?

Well, I know that I can always name the struct to solve the problem, I'm just curious about this, and wonder why it seems that this is not allowed.

解决方案

The fields of your anonymous struct type are unexported. This means you cannot create values of this struct and specify values for the fields from another package. Spec: Composite literals:

It is an error to specify an element for a non-exported field of a struct belonging to a different package.

If you change the struct definition to export the fields, then it will work because all fields can be assigned to by other packages (see Siu Ching Pong -Asuka Kenji-'s answer).

This is the case with the empty struct (with no fields) too: the empty struct has no fields, thus it has no unexported fields, so you're allowed to pass a value of that.

You can call the function with unmodified struct (with unexported fields) via reflection though. You can obtain the reflect.Type of the PrintAnonymous() function, and you can use Type.In() to get the reflect.Type of its first parameter. That is the anonymous struct we want to pass a value for. And you can construct a value of that type using reflect.New(). This will be a reflect.Value, wrapping a pointer to the zero value of the anonymous struct. Sorry, you can't have a struct value with fields having non-zero values (for reason mentioned above).

This is how it could look like:

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

This will print:

0: 

0 is zero value for int, and "" empty string for string.

For deeper inside into the type system and structs with unexported fields, see related questions:

Identify non builtin-types using reflect
How to clone a structure with unexported field?


Interestingly (this is a bug, see linked issue below), using reflection, you can use a value of your own anonymous struct type (with matching, unexported fields), and in this case we can use values other than the zero value of the struct fields:

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

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

Above runs (without panic):

1: Hello, world!

The reason why this is allowed is due to a bug in the compiler. See the example code below:

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)

Output is:

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

As you can see the unexported field i in both anonymous struct types (in main package and in somepackage as parameter to PrintAnonymous() function) –falsely– report the same package, and thus their type will be equal:

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

Note: I consider this a flaw: if this is allowed using reflection, then this should be possible without using reflection too. If without reflection the compile-time error is justified, then using reflection should result in runtime panic. I opened an issue for this, you can follow it here: issue #16616. Fix currently aims Go 1.8.

这篇关于导出带有匿名结构的函数作为参数[在package.Func的参数中不能使用value(type struct {...})作为类型struct {...}的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆