使用 “&” 的时机是什么,何时不使用?

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

go when to use & or not?

问题

我很困惑是否在使用Go语言声明变量并使用结构体进行初始化时需要使用&

假设我们有一个结构体包装器:

type HttpResult struct {
    Status int32       `json:"status"`
    Msg    string      `json:"msg"`
    Data   interface{} `json:"data,omitempty"` 
}

还有一个定义用户模型的结构体:

type OmUser struct {
    Id       primitive.ObjectID `json:"id" bson:"_id,omitempty"`
    Name     string             `json:"name"`
    Password string             `json:"password"`
    Email    string             `json:"email"`
}

以下两种声明方式似乎会得到相同的结果:

myOmUser := OmUser{ // 注意这里没有 & 符号
    Name:     "Tony",
    Password: "mypass",
    Email:    "tony@foo.com",
}

httpResult := &HttpResult{
    Status: 0,
    Msg:    "ok",
    Data:   myOmUser,
}

js, _ := json.Marshal(httpResult)

fmt.Println(js)

或者

myOmUser := &OmUser{ // 注意这里有 & 符号
    Name:     "Tony",
    Password: "mypass",
    Email:    "tony@foo.com",
}

httpResult := &HttpResult{
    Status: 0,
    Msg:    "ok",
    Data:   myOmUser,
}

js, _ := json.Marshal(httpResult)

fmt.Println(js)

那么,何时使用&以及为什么要使用呢?

英文:

I am confused whether to use a & with go when declaring a variable and init with a struct

say we have a struct wrapper

type HttpResult struct {
  	Status int32       `json:"status"`
	Msg    string      `json:"msg"`
	Data   interface{} `json:"data,omitempty"` 
}

and a struct defining the user model


type OmUser struct {
	Id       primitive.ObjectID `json:"id" bson:"_id,omitempty"`
	Name     string             `json:"name"`
	Password string             `json:"password"`
	Email    string             `json:"email"`
}

And the following declaring seems give the same result:

myOmUser := OmUser{ //note no & sign here
   Name: "Tony",
   Password: "mypass",
   Email: "tony@foo.com"
}

httpResult := &HttpResult{
		Status: 0,
		Msg:    "ok",
		Data:   myOmUser,
}

js, _ := json.Marshal(httpResult)

fmt.Println(js)

Or

myOmUser := &OmUser{ //note the & sign
   Name: "Tony",
   Password: "mypass",
   Email: "tony@foo.com"
}

httpResult := &HttpResult{
		Status: 0,
		Msg:    "ok",
		Data:   myOmUser,
}

js, _ := json.Marshal(httpResult)

fmt.Println(js)

so, when to use & and why?

答案1

得分: 1

在你的特定示例中,这并没有什么区别。
但是当我们看一个使用json.Unmarshal()的例子时,它就更有意义了:

jsonBlob := []byte(`{"id": "1", "name": "bob", "password": "pass", "email", "hi@me.com"}`)
var newOmUser OmUser
err := json.Unmarshal(jsonBlob, &newOmUser)
if err != nil {
    panic(err)
}

在这里,我们事先声明了变量,然后我们使用&将一个指向该变量的指针传递给Unmarshal函数。
这意味着Unmarshal函数可以访问并更新该变量,即使它在函数外部声明。
如果没有&Unmarshal函数将获得newOmUser变量的副本,并且会使我们声明的原始newOmUser变量为空。

当涉及到指针时,我的一般经验法则是:

除非必须使用,否则不要使用指针。

如果你需要使用任何解组函数,你将需要它们。还有很多其他使用它们的函数。

这是一个帮助我更好理解指针的快速练习:

func uppercase(s string) {
    s = strings.ToUpper(s)
    fmt.Println(s)
}

// 与uppercase()函数相同,但使用指针工作。
func uppercasePointer(s *string) {
    *s = strings.ToUpper(*s)
    fmt.Println(*s)
}

name := "bob"
uppercase(name)   // 输出'BOB'
fmt.Println(name) // 输出'bob' - 我们的变量没有改变

name2 := "bobpointer"
uppercasePointer(&name2) // 输出'BOBPOINTER'
fmt.Println(name2)       // 输出'BOBPOINTER' - 我们的变量已经改变

当我们调用uppercase(name)函数时,Go会复制name变量并将其发送到uppercase函数。
函数对接收到的副本所做的任何更改都会保留在函数内部。我们在函数外部声明的原始变量不会改变。

当我们调用uppercasePointer(&name2)函数时,我们发送了一个指向我们之前声明的name2变量的指针。
函数可以使用该指针来访问并更新我们之前声明的name2变量。

起初,你可能看不出指针的意义,但随着你继续使用Go,你会发现它们帮助我们解决一些复杂的问题。

英文:

In your particular example it doesn't make a difference.
But when we look at an example of using json.Unmarshal() it makes a bit more sense:

jsonBlob := []byte(`{"id": "1", "name": "bob", "password": "pass", "email", "hi@me.com"}`)
var newOmUser OmUser
err := json.Unmarshal(jsonBlob, &newOmUser)
if err != nil {
	panic(err)
}

Here we declare the variable before hand, and then we use the & to pass a pointer to that variable into the Unmarshal function.
That means that the Unmarshal function can reach out and update that variable, even though it's declared outside of the function.
Without the &, the Unmarshal function would get a copy of the newOmUser variable, and it would leave the original newOmUser variable that we declared empty.

When it comes to pointers, my general rule of thumb is:

> Don't use them unless you have to.

If you need to use any unmarshalling functions, you'll need them. There are lots of other functions that make use of them.

Here's a quick exercise that helps me understand a little more about pointers:

func uppercase(s string) {
	s = strings.ToUpper(s)
	fmt.Println(s)
}

// Same as the uppercase() function, but works with a pointer.
func uppercasePointer(s *string) {
	*s = strings.ToUpper(*s)
	fmt.Println(*s)
}

name := "bob"
uppercase(name)   // prints 'BOB'
fmt.Println(name) // prints 'bob' - our variable was not changed

name2 := "bobpointer"
uppercasePointer(&name2) // prints 'BOBPOINTER'
fmt.Println(name2)       // prints 'BOBPOINTER' - our variable was changed

When we call the uppercase(name) function, go makes a copy of the name variable and sends it to the uppercase function.
Whatever the function does to that copy that it received stays in the function. The original variable that we declared outside the function is not changed.

When we call the uppercasePointer(&name2) function, we are sending a pointer to the name2 variable we declared.
The function can use that pointer to reach out and update the name2 variable that we declared earlier.

At first, you might not see the point of pointers, but as you continue to use go, you will see that they help us solve some complex problems.

答案2

得分: 0

在Go语言中,空接口类型(empty interface)可以保存任意类型的值。所以在你的HttpResult.Data中,它是一个空接口类型,你可以给它赋予任意类型的值。

使用&定义变量的区别在于获取该类型的指针。这是Go语言中两种具有不同功能的类型。但是你可以将它们都赋值给空接口类型的变量,因为空接口类型可以接受任意类型的值。

package main

import (
	"fmt"
	"reflect"
)

type OmUser struct {
}

func main() {
	myOmUser := OmUser{}
	myOmUser2 := &OmUser{}
	fmt.Println(reflect.TypeOf(myOmUser)) //main.OmUser
	fmt.Println(reflect.TypeOf(myOmUser2)) //*main.OmUser
}

关于&的更多细节,请阅读Go文档中的地址操作符
> 对于类型为T的操作数x,地址操作&x会生成一个类型为*T的指针,指向x。操作数必须是可寻址的,也就是说,要么是一个变量、指针间接引用或切片索引操作;或者是可寻址的结构体操作数的字段选择器;或者是可寻址的数组的索引操作。作为对可寻址要求的例外,x也可以是一个(可能带括号的)复合字面量。如果对x的求值会导致运行时恐慌,那么对&x的求值也会如此。

英文:

Empty interface type in Go can hold values of any type. Tour here.

So in your HttpResult.Data is an empty interface type. So you can assigne any type to it.

The difference between defining a variable with & is getting a pointer of that type. Tour here.
Those are obviously two types with two different functionalities in Go. But you can assign both to empty interface type variable because its accepting values of any type.

package main

import (
	"fmt"
	"reflect"
)

type OmUser struct {
}

func main() {
	myOmUser := OmUser{}
	myOmUser2 := &OmUser{}
	fmt.Println(reflect.TypeOf(myOmUser)) //main.OmUser
	fmt.Println(reflect.TypeOf(myOmUser2)) //*main.OmUser
}

For more details about &, read Go doc address operators
> For an
> operand x of type T, the address operation &x generates a pointer of
> type *T to x. The operand must be addressable, that is, either a
> variable, pointer indirection, or slice indexing operation; or a field
> selector of an addressable struct operand; or an array indexing
> operation of an addressable array. As an exception to the
> addressability requirement, x may also be a (possibly parenthesized)
> composite literal. If the evaluation of x would cause a run-time
> panic, then the evaluation of &x does too.

答案3

得分: 0

fooA := &Foo{}

fooA 的类型是 *Foo

fooB := Foo{}

fooB 的类型是 Foo

在实践中,这意味着如果你有一个接受 *Foo 类型的函数,你可以使用以下任一方式...

func someFunc(f *Foo) {
    // ...
}

fooA := &Foo{}
someFunc(fooA)

fooB := Foo{}
someFunc(&fooB)

所以实际上,你可以根据需要创建任何一种类型。

英文:
fooA := &Foo{}

fooA has type *Foo.

fooB := Foo{}

fooB has type Foo.

https://tour.golang.org/moretypes/1

In practice, this means if you had a func that accepted type *Foo you could do either of the following...

func someFunc(f *Foo) {
    // ...
}

fooA := &Foo{}
someFunc(fooA)

fooB := Foo{}
someFunc(&fooB)

So realistically, create whichever you need to be honest.

huangapple
  • 本文由 发表于 2021年9月3日 12:58:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/69039715.html
匿名

发表评论

匿名网友

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

确定