> For the complete documentation index, see [llms.txt](https://denglj.gitbook.io/essential-go/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://denglj.gitbook.io/essential-go/10-structs/duplicate-a-struct-make-a-copy.md).

# 复制结构体(创建一份拷贝)

结构体的复制用赋值语句这种简单的形式。

{% tabs %}
{% tab title="Go" %}

```go
package main

import "fmt"

type T struct {
	I int
	S string
}

func main() {
	// initialize a struct
	t := T{1, "one"}

	// make struct copy
	u := t // u has its field values equal to t

	if u == t { // true
		fmt.Println("u and t are equal") // Prints: "u and t are equal"
		fmt.Printf("u address is: %p\nt address is:%p\n", &u, &t)
	}
}
```

{% endtab %}

{% tab title="Output" %}

```
u and t are equal
u address is: 0xc00000c0c0
t address is:0xc00000c0a0
```

{% endtab %}
{% endtabs %}

:point\_right: [点击此处](https://play.golang.org/p/gcJ3c-BQjET) :point\_left: 亲自在编程操场测试上述代码。

上例中，`t` 和 `u` 就是独立的对象(结构体)了。

因为类型`T`中不包含任何引用类型(切片slice、映射map、数据通道channel)作为它的字段，因此对`t`和`u`的值进行修改并不会彼此影响。

```go
	t.I, u.I = 100, 1
	fmt.Printf("t.I = %d, u.I = %d\n", t.I, u.I)
```

**然而**，假如`T`中包含的有引用类型，比如：

```go
type T struct {
    I  int
    S  string
    xs []int // a slice is a reference type
}
```

如果还只是简单地用赋值语句来拷贝对象，其结果是两个不同的对象引用了相同的切片。

```go
// initialize a struct
t := T{I: 1, S: "one", xs: []int{1, 2, 3}}

// make struct copy
u := t // u has its field values equal to t
```

因为`u`和`t`都指向了相同的切片，因此在一个变量中修改了切片，另一个变量也会受影响而改变。

```go
// update a slice field in u
u.xs[1] = 500

fmt.Printf("t.xs = %d, u.xs = %d\n", t.xs, u.xs)
// t.xs = [1 500 3], u.xs = [1 500 3]
```

因此，**在复制包含引用类型的结构体时需要十分小心**，记住这个特性，否则可能会出现意料不到的错误。

如果要结构体中引用类型字段也真实拷贝独立存在，修改其中一个而不影响另一个，需要像下面这样显式地完成复制：

```go
// explicitly initialize u's slice field
u.xs = make([]int, len(t.xs))
// copy the slice values over from t
copy(u.xs, t.xs)

// updating slice value in u will not affect t
u.xs[1] = 500

fmt.Printf("t.xs = %d, u.xs = %d\n", t.xs, u.xs)
// t.xs = [1 2 3], u.xs = [1 500 3]
```

{% hint style="info" %}

#### 译者注

本节谈论的利用赋值语句隐式拷贝和make函数显式拷贝的区别，实际上就是浅拷贝和深拷贝的区别。

在用赋值语句拷贝时，系统虽然为`u.xs`分配了新内存地址用于存放值，此地址与`t.xs`不同，但所存放的值并不是具体的切片`[1 2 3]`，而是原切片的指针。在赋值语句发生拷贝的瞬间，复制了指针，而不是复制了值，所以指向了相同的内存空间。
{% endhint %}

![赋值语句拷贝示意图，xs字段直接存放的是切片的指针而非切片的值](/files/-M5rRtu8Gzyqyvrcq_pD)
