在这些情况下使用指针还是不使用指针?

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

To use pointers or not to use in these situations?

问题

第一部分:

在第一部分中,有两种处理指针的方式。

第一种方式:

// 第一种方式
struct User {
    client   http.Client,
    jar      cookiejar.Jar,
    username string,
    password string,
}
func NewUser(username, password string) *User {
    jar := cookiejar.New(nil)
    client := http.Client{ Jar: &jar }
    return &User{ username: username, password: password, client: client, jar: jar }
}

第二种方式:

// 第二种方式
struct User {
    client   *http.Client, //!
    jar      *cookiejar.Jar, //!
    username string,
    password string,
}
func NewUser(username, password string) *User {
    jar := &cookiejar.New(nil) //!
    client := &http.Client{ Jar: jar } //!
    return &User{ username: username, password: password, client: client, jar: jar } //!
}

第二部分:

在第二部分中,也有两种方式。

第一种方式:

func (u *User)process() {
    u.client.Get("https://example.com")
    fmt.Printf("%v", u.jar.Cookies("https://example.com"))
}

// 第一种方式
users := make([]*User, 256)
// ... 设置 users
for user := range users {
    go user.process()
}

第二种方式:

func (u *User)process() {
    u.client.Get("https://example.com")
    fmt.Printf("%v", u.jar.Cookies("https://example.com"))
}

// 第二种方式
users := make([]User, 256) //!
// ... 设置 users
for user := range users {
    go user.process()
}

哪种方式对于每个部分来说更好?为什么?或者是否有更好的方式来完成所有这些操作?

英文:

I'm building a (deep parser?) and have two parts of the code, in each of those I have two ways to work with pointers:

The first part:

// The first way
struct User {
    client   http.Client,
    jar      cookiejar.Jar,
    username string,
    password string,
}
func NewUser(username, password string) *User {
    jar := cookiejar.New(nil)
    client := http.Client{ Jar: &jar }
    return &User{ username: username, password: password, client: client, jar: jar }
}

// The second way
struct User {
    client   &http.Client, //!
    jar      &cookiejar.Jar, //!
    username string,
    password string,
}
func NewUser(username, password string) *User {
    jar := &cookiejar.New(nil) //!
    client := &http.Client{ Jar: jar } //!
    return &User{ username: username, password: password, client: client, jar: jar } //!
}

(Changed lines have a comment //! in the end)

The second part:

func (u *User)process() {
    u.client.Get("https://example.com")
    fmt.Printf("%v", u.jar.Cookies("https://example.com"))
}

// The first way
users := make([]&User, 256)
// ... setting users
for user := range users {
    go user.process()
}

// The second way
users := make([]User, 256) //!
// ... setting users
for user := range users {
    go user.process()
}

Which way is better for each part and why?
Or is there an even better way to do all this?

答案1

得分: 1

在容器中使用指针的主要目的是,在复制容器时,指向的值不会被复制。如果复制对象不安全(例如互斥锁)或者太昂贵(例如大型数组),那么使用指针可能是一个好主意。

在你的情况下,你将用户存储在一个切片中。需要记住的是,使用切片时,每次在切片上执行append操作时,可能会复制整个切片内容,也可能不会复制。这意味着,如果你要使用append(或copy或其他复制值的方法),你的数据类型必须是可复制的:要么通过使数据类型本身具备可复制性,要么使用指针。

如果复制值没有问题,那么最好使用非指针值,因为这会简化你的程序:

  • 零值可用(而不是nil)
  • 需要更少的分配
  • 简化或消除构造函数代码

如果你的切片不需要被复制(append或其他方式),并且你只通过调用这个接受指针接收器的process方法来使用User值,那么不会发生复制,你就不用担心了。

如果可能发生复制,那么问题是:你的User结构体是否具备可复制性?很可能是的。http.Client文档没有关于不具备可复制性的特定说明,所以只有在直接修改其中一个字段时才会不安全。

下面是你的代码的简化版本:

type User struct {
    client   http.Client
    username string
    password string
}


func (u *User) setup() {
    user.username = something
    user.password = something
    user.client.Jar, err := cookiejar.New(nil)
    if err != nil {
        // 处理错误
    }
}

func (u *User) process() {
    // 执行一些请求
    fmt.Println(u.client.Jar.Cookies("https://example.com"))
}

func run() {
    users := make([]User, 256)
    for _, user := range users {
        user.setup()
        go user.process()
    }
    // 做一些其他操作...
}

如果你对你的User类型是否具备或将具备可复制性不确定,你可以简单地将切片改为指针的切片,并继续使用指针接收器的方法,这样你就没问题了。

func run() {
    users := make([]*User, 256)
    for i := range users {
        users[i] = new(User)
        users[i].setup()
        go users[i].process()
    }
    // 做一些其他操作...
}
英文:

The main point of using pointers in containers is that it when the container is copied, the pointed-to value is not copied. If the copying of an object is either not safe (like a mutex), or too expensive (like a large array), then it could be a good idea to use a pointer.

In your case, you are storing your users in a slice. It's important to remember with slices that any time you append on the slice, it may or may not copy the entire slice contents. This means that if you are ever to use append (or copy, or other methods of copying the values), your data type must be copy-safe: either by making the data type copy-safe in itself, or by using a pointer to it.

If there is no issue with copying the values, then you may as well use non-pointer values, as it will simplify your program:

  • Zero-values will be usable (instead of nil)
  • Fewer allocations required
  • Simplify or eliminate constructor code

If your slice does not need to be copied (append or otherwise), and you only use User values by calling this process method which takes a pointer receiver, then no copies will occur and you have nothing to worry about.

If copies may occur then the question is: is your User struct copy-safe? Most likely, yes. The http.Client documentation doesn't have any specific note about not being copy-safe, so it would only be unsafe if you directly modify one of its fields.

Here's what a simplified version of your code might look like:

type User struct {
    client   http.Client
    username string
    password string
}


func (u *User) setup() {
    user.username = something
    user.password = something
    user.client.Jar, err := cookiejar.New(nil)
    if err != nil {
        // handle error somehow
    }
}

func (u *User) process() {
    // do some request
    fmt.Println(u.client.Jar.Cookies("https://example.com"))
}

func run() {
    users := make([]User, 256)
    for _, user := range users {
        user.setup()
        go user.process()
    }
    // do something ...
}

If you're unsure about if your User type is or will be copy-safe, you can simply change to slice of pointers and continue using pointer-receiver methods and you will be fine.

func run() {
    users := make([]*User, 256)
    for i := range users {
        users[i] = new(User)
        users[i].setup()
        go users[i].process()
    }
    // do something ...
}

huangapple
  • 本文由 发表于 2021年7月18日 03:10:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/68423568.html
匿名

发表评论

匿名网友

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

确定