在函数内部定义结构与外部的含义?

时间:2017-02-02 18:52:10

标签: go

在函数内部定义struct与在外部定义它是否有任何影响(GC流失,性能或其他方面)?例如:

type Outside struct {
  Foo string `json:"foo"`
}

func SomeFunc(b []byte) error {
  outside := Outside{}

  if err := json.NewDecoder(b).Decode(&outside); err != nil {
    return err
  }

  ...
}

VS

func SomeFunc(b []byte) error {

  type inside struct {
    Foo string `json:"foo"`
  }

  if err := json.NewDecoder(b).Decode(&inside); err != nil {
    return err
  }

  ...
}

是否会出现一种优先于另一种情况的情况?

7 个答案:

答案 0 :(得分:6)

对我来说,在函数中定义的类型的主要缺点是无法在该类型上定义方法。

请参见以下示例https://play.golang.org/p/cgH01cRwDv6

package main

import (
    "fmt"
)

func main() {
    type MyType struct {
        Name string
    }

    // You cannot define a method on your type
    // defined in a function, can you?

    func (m MyType) String() string {
      return m.Name
    }

    m := MyType{Name: "Hello, World!"}
    fmt.Println(m)
}

以上示例将失败,并显示错误prog.go:15:27: expected ';', found 'IDENT' string (and 1 more errors)

答案 1 :(得分:5)

没有性能差异 - 它只是范围的差异(即,可以看到类型定义的位置)。如果你只需要一个函数中的类型,可以在那里定义它。

正如其他人所指出的那样,如果您在包级别(即函数外部)定义一个名称以大写字母开头的类型,它将被导出(即在包外可见)。如果名称不以大写字母开头,则只会在包中显示。

答案 2 :(得分:3)

我的理解是区别在于可访问性。以大写字母开头定义的结构将是可导出的,这意味着可以从其他包访问它。以小写字母开头定义的结构可以从同一个包中的任何内容访问,但不能从外部访问。在函数中定义的结构只能由该函数访问/初始化。

答案 3 :(得分:1)

正如其他人所提到的,它完全是关于限制变量范围的。如果要在函数内使用结构体,也可以使用匿名结构体。

package main

import (
    "fmt"
)

func main() {

    m := struct {
        greeting string
        name     string
    }{
        greeting: "hello",
        name:     "world",
    }

    fmt.Printf("%v %v\n", m.greeting, m.name)
}

如果您只想在函数内部使用结构体,您可以定义结构体的字段并立即为其赋值。

答案 4 :(得分:0)

对我来说,我曾经在函数内部定义了一个结构,用于将json [] byte编组到该结构实例中并从该实例中提取一条消息。

显然,不需要定义该结构。我可以通过将json字节数组编组到interface {}中来提取消息,然后递归进行强制转换以获取所需的消息。

通过定义结构,提取消息变得非常容易:)

    var errDetail struct {
        Message string `json:"message"`
        Success bool   `json:"success"`
    }

    json.Unmarshal(*bytes, &errDetail)

    if errDetail.Message == "" {
        fmt.Println("error message is not present")
        return nil
    }
    return errDetail.Message

答案 5 :(得分:0)

对于Konrad Kleine的问题,您仍然可以通过以下解决方法来解决 https://play.golang.org/p/50yv66LUNRt

package main

import (
    "fmt"
)

func main() {
    type MyType struct {
        Name string
        String func() string
    }
    InitMyType := func(m *MyType) {
        m.String = func() string {
            return m.Name
        }
        return 
    }
    
    m := MyType{Name: "Hello, World!"}
    initMyType(&m)
    
    fmt.Println(m.String())
}

我同意区别只是可访问性。

但这仍然非常有用,特别是您想要的只是一个临时结构,或者当您进行单元测试时,有很多类似的结构,例如args,包中的测试用例,您无需费心为它们命名一一。

答案 6 :(得分:0)

范围不同,您可以在此处查看 Golang 规范:enter link description here

最相关的部分是:

<块引用>

Go 的词法范围使用块:

表示在顶层(任何函数之外)声明的常量、类型、变量或函数(但不是方法)的标识符的范围是包块。

在函数内声明的类型标识符的范围从 TypeSpec 中的标识符开始,到最里面的包含块的末尾结束。

如果你使用go tool compile -S -N hello.go来检查生成的汇编代码,你可以发现包级别定义的类型名称和函数内部定义的类型名称不同。

包级别

package main

import (
    "fmt"
)

type Point struct {
    X, Y int
}

func main() {
    fmt.Printf("%v\n", Point{X: 1, Y: 2})
}

然后尝试编译并找到这一行:type."".Point SRODATA size=144

内部函数

package main

import (
    "fmt"
)


func main() {
    type Point struct {
        X, Y int
    }
    fmt.Printf("%v\n", Point{X: 1, Y: 2})
}

然后尝试找到这一行:type.*"".Point·1 SRODATA size=56

也就是说,它们只是在编译后得到了不同的名称。