dongshang1529 2016-08-05 08:44
浏览 83
已采纳

导出带有匿名结构作为参数的函数[不能在package.Func的参数中将值(结构{…}类型)用作结构{…}类型]

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

package main

import (
    "fmt"
)

func PrintAnonymous(v struct {
    i int
    s string
}) {
    fmt.Printf("%d: %s
", 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.

  • 写回答

2条回答 默认 最新

  • dsgk40568 2016-08-05 09:33
    关注

    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
    ", 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)
    

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么
  • ¥15 banner广告展示设置多少时间不怎么会消耗用户价值
  • ¥16 mybatis的代理对象无法通过@Autowired装填
  • ¥15 可见光定位matlab仿真
  • ¥15 arduino 四自由度机械臂
  • ¥15 wordpress 产品图片 GIF 没法显示
  • ¥15 求三国群英传pl国战时间的修改方法
  • ¥15 matlab代码代写,需写出详细代码,代价私
  • ¥15 ROS系统搭建请教(跨境电商用途)
  • ¥15 AIC3204的示例代码有吗,想用AIC3204测量血氧,找不到相关的代码。