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?
CodePudding user response:
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: "[email protected]",
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 = "[email protected]",
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.