Home > Net >  How to use a struct field into another struct without referring to it as a key
How to use a struct field into another struct without referring to it as a key

Time:12-10

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.

  • Related