Go语言 如何复制结构体

5ssjco0h  于 2022-12-07  发布在  Go
关注(0)|答案(5)|浏览(397)

我想复制一个对象,这样我就有了两个相同的对象,但内存地址不同。我的第一次尝试失败了:

aa := a
assert.NotEqual(t, &a, &aa, "Copied items should not be the same object.") // Test fails

我能修正这个问题,使它真正的复制结构体吗?这个结构体没有什么特别的。

0s0u357o

0s0u357o1#

在go中,基元类型和只包含基元类型的结构体是按值复制的,所以你可以通过简单地赋值给一个新变量(或者从一个函数返回)来复制它们。例如:

type Person struct{
  Name string
  Age  int
}

alice1 := Person{"Alice", 30}
alice2 := alice1
fmt.Println(alice1 == alice2)   // => true, they have the same field values
fmt.Println(&alice1 == &alice2) // => false, they have different addresses

alice2.Age += 10
fmt.Println(alice1 == alice2)   // => false, now they have different field values

请注意,正如评论者所提到的,示例中的混乱可能是由于所使用的测试库的语义造成的。
如果你的结构体碰巧包含数组、切片或指针,那么你就需要对被引用的对象执行深度复制,除非你想保留副本之间的引用。Golang没有提供内置的深度复制功能,所以你必须实现自己的深度复制功能,或者使用许多免费提供的库中的一个。

igsr9ssn

igsr9ssn2#

DeepCopy是一个非常繁重的操作,因此应尽可能避免。对于复杂的结构,如以下所示,我们可以优化代码。

type Address struct {
    city  string
    state string
}

type Person struct {
    age     int
    name    string
    address []Address
}

p := Person{
    age:  20,
    name: "Jack",
    address: []Address{
        {
            city:  "city1",
            state: "state1",
        }, {
            city:  "city2",
            state: "state2",
        },
    },
}

var q Person
q.age = p.age
q.name = p.name
q.address = append(q.address, p.address...)
q.address[0].city = "cityx"

结果:

p object:
{20 Jack [{city1 state1} {city2 state2}]}

q object:
{20 Jack [{cityx state1} {city2 state2}]}

推论:
从上面的例子可以看出,当q改变时,p对象并没有改变。这种方法可以用在嵌套的结构体数组中。

mklgxw1f

mklgxw1f3#

注意,如果你的源代码struct实际上是一个指针,那么普通的赋值就不起作用了:

package main
import "net/http"

func main() {
   a, err := http.NewRequest("GET", "https://stackoverflow.com", nil)
   if err != nil {
      panic(err)
   }
   b := a.URL
   b.Host = "superuser.com"
   println(a.URL.Host == "superuser.com")
}

相反,您需要取消对指针的引用:

package main
import "net/http"

func main() {
   a, err := http.NewRequest("GET", "https://stackoverflow.com", nil)
   if err != nil {
      panic(err)
   }
   b := *a.URL
   b.Host = "superuser.com"
   println(a.URL.Host == "stackoverflow.com")
}
8iwquhpp

8iwquhpp4#

您可以使用函式以传值方式传递,并依据您的需求,原封不动地传回或变更参数。
使用上面的结构体:

func main() {
    copyOf := func(y Person) Person {
        y.name = "Polonius"
        y.address = append(y.address, Address{
            city:  "other city",
            state: "other state",
        })
        return y
    }

    p := Person{
        age:  20,
        name: "Jack",
        address: []Address{
            {
                city:  "city1",
                state: "state1",
            }, {
                city:  "city2",
                state: "state2",
            },
        },
    }

    q := copyOf(p)

    fmt.Printf("Orig %v, \naddrss: %p \n\n", p, &p)
    fmt.Printf("Copy %v, \naddress: %p\n\n", q, &q)
}
lc8prwob

lc8prwob5#

您可以尝试gob.Encode,然后尝试gob.Decode,就像我们在javascript中所做的那样:首先是JSON.stringify,然后是JSON.parse
在golang中,你应该使用gob而不是json.Marshal/json.Unmarshal,因为你不需要在字段上添加json标记,并且你可以通过gob来处理接口。

import (
    "bytes"
    "encoding/gob"
)

func DeepCopy(src, dist interface{}) (err error){
    buf := bytes.Buffer{}
    if err = gob.NewEncoder(&buf).Encode(src); err != nil {
        return
    }
    return gob.NewDecoder(&buf).Decode(dist)
}

dist应始终为指针

相关问题