你无法这样做。这就是未导出字段的目的:只有声明该字段的包才能修改它们。
请注意,如果T类型在另一个包中声明,则甚至不能编写以下代码:
p := somepackage.T{"some string", []int{10, 20}}
因为这样会隐式地尝试设置未导出的
T.is
字段,从而导致编译时错误:
implicit assignment of unexported field 'is' in somepackage.T literal
如果您拥有(或者可以修改)该程序包,最好提供一个
Clone()
方法或函数,或为类型
T
提供一个
SetIs()
方法。如果第三方程序包没有提供这样的功能,则无能为力。
请注意,虽然使用
unsafe
程序包可以做到这一点,但正如其名称所示:它是
不安全的,您应该远离它。
另请注意,您可以创建
T
类型的新值,其中
is
未被复制,但将成为其类型的
零值(在
[]int
的情况下将为
nil
):
var r somepackage.T
s := somepackage.T{S: p.S}
fmt.Printf("%q\n", r)
fmt.Printf("%q\n", s)
将输出:
{"" []}
{"some string" []}
但是你不能为未导出的字段 T.is
设置任何非零值。
请注意,您可以通过将其分配给另一个具有相同类型的结构变量(即“精确”复制)来制作具有未导出字段的结构的副本,这样会正确地复制未导出的字段。
就像这个例子:
type person struct {
Name string
age *int
}
age := 22
p := &person{"Bob", &age}
fmt.Println(p)
p2 := new(person)
*p2 = *p
fmt.Println(p2)
这将输出(在 Go Playground上尝试):
&{Bob 0x414020}
&{Bob 0x414020}
我们甚至可以使用
reflect
来进行泛化,而不依赖于具体的类型:
type person struct {
Name string
age *int
}
age := 22
p := &person{"Bob", &age}
fmt.Println(p)
v := reflect.ValueOf(p).Elem()
vp2 := reflect.New(v.Type())
vp2.Elem().Set(v)
fmt.Println(vp2)
请在Go Playground上尝试这个。
但我们无法做到的是改变未导出的person.age
字段指向其他内容。除非有声明包的帮助,否则它只能为nil
或相同的指针值(指向原始字段的对象)。