在函数内部定义结构体和在函数外部定义结构体之间的区别是什么?

huangapple go评论81阅读模式
英文:

Implications of defining a struct inside a function vs outside?

问题

在函数内部定义结构体与在函数外部定义结构体相比,是否会有任何影响(如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
  }

  ...
}

func SomeFunc(b []byte) error {

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

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

  ...
}

是否存在某种情况下,一个比另一个更受青睐?

英文:

Are there any implications (GC churn, performance, or otherwise) to defining a struct inside a function vs. having it defined outside? For example:

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
  }

  ...
}

Would there be any situation where one is preferred over the other?

答案1

得分: 28

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

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

package main

import (
	"fmt"
)

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

	// 你不能在函数中定义的类型上定义方法,对吗?

	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)

英文:

To me the main drawback for a type defined in a function is that you cannot define methods on that type.

See this example 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)
}

The above example will fail with the error prog.go:15:27: expected ';', found 'IDENT' string (and 1 more errors).

答案2

得分: 20

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

正如其他人指出的,如果你在包级别定义一个以大写字母开头的名称的类型(即在函数外部),它将被导出(即在包外可见)。如果名称不以大写字母开头,它只能在包内可见。

英文:

There is no performance difference – it's only a difference of scope (i.e., where the type definition can be seen). If you only need the type within a single function, it's fine to define it there.

As others have noted, if you define a type at the package level (i.e., outside of a function) with a name beginning with a capital letter, it will be exported (i.e., visible outside the package). If the name doesn't begin with a capital letter, it will only be visible within the package.

答案3

得分: 9

我的理解是,区别只在于可访问性:

  • 大写字母开头定义的结构体是可导出的,也就是说可以从其他包中访问。
  • 小写字母开头定义的结构体只能在同一包内部访问,而不能从外部访问。
  • 在函数内部内联定义的结构体只能由该函数访问/初始化。
英文:

My understanding is the difference is just in accessibility.

  • A struct defined starting with an upper case letter will be exportable, meaning it can be accessed from other packages.
  • A struct defined starting with a lower case letter can be accessed from anything within the same package but not externally.
  • A struct defined in a function inline can only be accessed/initialized by that function.

答案4

得分: 3

对于我来说,我曾经在一个函数内部定义了一个结构体,用于将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
英文:

For me I once defined a struct inside a function for marshalling JSON byte array ([]byte) into the struct instance, and extract a message from the instance.

Obviously it is not required to define the struct. I could have extracted the message by marshalling the JSON byte array into interface{} and then cast recursively to get the required message.

By defining the struct, extraction of the message becomes very easy 在函数内部定义结构体和在函数外部定义结构体之间的区别是什么?

    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


</details>



# 答案5
**得分**: 1

作用域是不同的,你可以在Golang规范中查看[这里][1]:

最相关的部分是:

> Go使用块进行词法作用域:
>
> 在顶层(任何函数之外)声明的常量、类型、变量或函数(但不包括方法)的标识符的作用域是包块。
>
> 在函数内部声明的类型标识符的作用域从TypeSpec中的标识符开始,到最内层包含块的结束为止。

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

**包级别**

```golang
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

这意味着它们在编译后得到了不同的名称。

英文:

The scope is different, you can check in Golang spec here:

Most related parts are:

> Go is lexically scoped using blocks:
>
> The scope of an identifier denoting a constant, type, variable, or function (but not method) declared at top level (outside any function) is the package block.
>
> The scope of a type identifier declared inside a function begins at the identifier in the TypeSpec and ends at the end of the innermost containing block.

And if you use go tool compile -S -N hello.go to check the generated assembly code, you can spot the names of type defined in package level and names of type defined inside function are different.

Package level

package main

import (
	&quot;fmt&quot;
)

type Point struct {
	X, Y int
}

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

Then try to compile and find this line: type.&quot;&quot;.Point SRODATA size=144.

Inside function

package main

import (
	&quot;fmt&quot;
)


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

Then try to find this line: type.*&quot;&quot;.Point&#183;1 SRODATA size=56

Than means, they just got different names after compiled.

答案6

得分: 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)
}

如果你只打算在函数内部使用该结构体,你可以定义结构体的字段并立即给它们赋值。

英文:

As others have mentioned its all about limit the variables scope. If you are going to use a struct inside a function, you could also use an anonymous struct.

package main

import (
	&quot;fmt&quot;
)

func main() {

	m := struct {
		greeting string
		name     string
	}{
		greeting: &quot;hello&quot;,
		name:     &quot;world&quot;,
	}

	fmt.Printf(&quot;%v %v\n&quot;, m.greeting, m.name)
}

If you're only planing to use the struct inside the function you can define the fields of the struct and assign values to them right away.

答案7

得分: 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())
}

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

但这仍然非常有用,特别是当你只需要一个临时结构体,或者在进行单元测试时,一个包中有许多类似的结构体,你不需要一个个为它们命名。

英文:

For Konrad Kleine's problem, you can still do it with some workaround like this
https://play.golang.org/p/50yv66LUNRt

package main

import (
	&quot;fmt&quot;
)

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: &quot;Hello, World!&quot;}
	initMyType(&amp;m)
	
	fmt.Println(m.String())
}

I agree the difference is just accessibility.

But this is still very useful, especially what you want is only a temp struct, or when you do unit test, there are many similar struct like args, test case in a package, you won't need bother to name them one by one.

huangapple
  • 本文由 发表于 2017年2月3日 02:52:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/42010112.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定