英文:
How to use a struct field into another struct without referring to it as a key
问题
我想在另一个结构体中插入一个结构体字段,而不必使用结构体名称。
我知道可以这样做:
type Person struct {
Name string
}
type User struct {
Person
Email, Password string
}
但这会导致以下结构体:
user := User{Person: Person{Name: ""}, Email: "", Password: ""}
我该如何做到像这样:
type Person struct {
Name string
}
type User struct {
Name Person.Name // 这里
Email, Password string
}
以便像这样使用它:
user := User{Name: "", Email: "", Password: ""}
这种做法可行吗?
英文:
I want to insert a struct field into another struct without having to use the struct name.
I know that I can do this:
type Person struct {
Name string
}
type User struct {
Person
Email, Password string
}
But It results in this struct:
user := User{Person: Person{Name: ""}, Email: "", Password: ""}
How can I do something like this:
type Person struct {
Name string
}
type User struct {
Name Person.Name // Here
Email, Password string
}
To use it like this
user := User{Name: "", Email: "", Password: ""}
Is it possible?
答案1
得分: 1
简单来说,根据当前的语言实现,你无法以你期望的方式初始化内部的Person。
当初始化一个字面量时,你需要明确指定(或者说:字面量!)。由于User包含一个Person,一个字面量的User必须包含一个字面量的Person,就像你所示例的那样:
u := User{
Person: Person{
Name: "Bob",
},
Email: "bob@bobspot.com",
Password: "you're kidding right?",
}
然而,一旦你有了一个类型为User的变量,你可以利用匿名字段来设置(或获取)匿名Person的Name,如下所示:
u := User{}
u.Name = "Bob"
u.Email = "bob@bobspot.com",
u.Password = "you're kidding right?",
为什么Go要让我做这么多工作?
让我们想象一下,如果可以以你期望的方式初始化内部的Person:
u := User{ Name: "Bob" }
现在让我们进一步想象,我们稍后修改User结构并添加它自己的Name字段:
type User struct {
Person
Name string
Email string
Password string
}
现在你可以明显地初始化新的Name字段:
u := User{ Name: "Bob" }
注意,这与之前初始化User.Person.Name的代码是相同的,但现在它初始化的是User.Name。这样不好。
更多陷阱
使用这样的代码还有其他潜在的问题。
首先,向User变量的未限定引用添加一个Name字段已经类似地“破坏”了对User变量上的Name的设置:
u.Name = "Bob" // 用于设置User.Person.Name,现在设置的是User.Name
此外,只有一个匿名的Person字段时,默认情况下,User.Person.Name字段会被默认地转换为JSON的“Name”字段:
{
"Name": "",
"Email": "",
"Password": ""
}
如果添加一个Name字段,那么这个字段将作为"Name"被转换为JSON,而User.Person.Name字段将根本不会被转换。
你可能会认为可以为User.Person.Name添加一个json标签,例如:
type User struct {
Person `json:"PersonName"`
Name string
Email string
Password string
}
但是现在Person将被转换为一个带有Name字段的对象:
{
"PersonName": {
"Name": ""
},
"Name": "",
"Email": "",
"Password": ""
}
即使User没有Name字段,如果你尝试更改匿名Person的转换字段名,也会发生这种情况。
简而言之:在结构体中使用匿名结构体作为“添加字段”的方式可能存在问题,而且可能很脆弱,应该尽量避免使用。
英文:
Simply put, with the current language implementation you can't.
When initialising a literal you need to be explicit (or, put another way: literal! [sic]). Since a User contains a Person, a literal User must contain a literal Person, as you illustrate:
u := User{
Person: Person{
Name: "Bob",
},
Email: "bob@bobspot.com",
Password: "you're kidding right?",
}
However, once you have a variable of type User, you can then leverage the anonymous field to set (or get) the Name of the anonymous Person with the User:
u := User{}
u.Name = "Bob"
u.Email = "bob@bobspot.com",
u.Password = "you're kidding right?",
Why Does Go Make Me Do All This Work?
Let us imagine that it was possible to initialise the inner Person in the way you are looking for:
u := User{ Name: "Bob" }
Now let us further imagine that we later modify the User struct and add its own Name field:
type User struct {
Person
Name string
Email string
Password string
}
And now you can obviously initialise the new Name field:
u := User{ Name: "Bob" }
Notice that this is identical to the previous code that initialised User.Person.Name but now it is initialising User.Name. Not good.
More Gotchas
There are further traps lurking with code like this.
First, the addition of a Name field in User already similarly "breaks" unqualified references to Name on User variables:
u.Name = "Bob" // used to set User.Person.Name, now sets User.Name
Also, with only an anonymous Person field, the User.Person.Name field is marshalled to JSON by default as a "Name" field:
{
"Name": "",
"Email": "",
"Password": ""
}
If a Name field is added, then this is the field that is marshalled as "Name" and the User.Person.Name field is not marshalled at all.
You might think you can add a json tag for the User.Person.Name, e.g.
type User struct {
Person `json:"PersonName"`
Name string
Email string
Password string
}
But now the Person is marshalled as an object with a Name field:
{
"PersonName": {
"Name": ""
},
"Name": "",
"Email": "",
"Password": ""
}
This also happens if you try to change the marshalled field name of the anonymous Person even if User does not have a Name field.
In short: using anonymous structs within structs as a way of "adding fields" is potentially problematic and fragile and should probably be avoided.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论