Skip to content

proposal: Go 2: Destructuring operator to assign fields in different types of the structs #33957

Closed
@rodcorsi

Description

@rodcorsi

It is normal to have a situation where we need to assign a struct using fields from another struct, an example is when the request or response type is different from the storage type. In these situations we can simply write:

data := DataModel {
  ID:    requestData.ID,
  Value: requestData.Value,
  ...
}

The problems start when we have a struct with many fields or new fields is added and we forgot to handle it.
We could use reflection to copy fields with the same name and type from a struct to another, but is slow e the code little complex

I'm trying to propose a language change to safely copy fields from a struct to another.
The idea is to use ... operator to copy the fields with the same name and type to another struct.

Example:

type RequestData struct {
  ID    string
  Value string
}

type DataModel struct {
  ID    string
  Value string
  Other bool
  AnyID int
}

Uses the RequestData fields to create a new instance of DataModel.
Important to observe that all fields need to be handled when using the ... operator.
In this case, the Other and AnyID need to be set.

requestData := RequestData{ID: "1", Value: "foo" }

data := DataModel{ Other: false, AnyID: 5, requestData... }
fmt.Printf("%#v\n", data) // DataModel{ID:"1", Value:"foo", Other:false, AnyID:5}

// it won't compile
// data := DataModel{ requestData... }

This example won't compile because DataModel has different fields from RequestData.
The fields Other and AnyID don't exist in RequestData.

requestData2 := RequestData{ data... } // it won't compile

It creates a new instance of DataModel but changes the field Value

data2 := DataModel{Value: "bar", data...}
fmt.Printf("%#v\n", data2) // DataModel{ID:"1", Value:"bar", Other:false, AnyID:5}

Embedded structs
When you use ... operator, the embedded struct fields will be handled as a normal field.

type A struct { i int }
type B struct { A }
type C struct { i int }

b1 := B{A{5}}
c1 := C{b1...} // c1 := C{i: b1.i}
b2 := B{c1...} // b2 := B{A{i: c1.i}}
fmt.Println(b1.i, c1.i, b2.i) // 5 5 5

Destructuring fields

type RequestWithExtraData struct {
  ID        string
  Value     string
  RequestID int
}
requestData := RequestWithExtraData{ "1", "foo", 5 }
RequestWithExtraData{ ID:id, Value:value } := requestData
fmt.Println(id, value) // 1 foo

Destructuring with rest.
The field RequestID will be ignored (_) and the rest of the fields it will assign the new DataModel instance, but is required to set Other and AnyID

requestWithExtraData := RequestWithExtraData{ ID:"3", Value:"extra", RequestID:42 }

RequestWithExtraData{
    RequestID: _, // ignore RequestID
    ...DataModel{
        Other: true,
        AnyID: 0,
    }: rest,
} := requestWithExtraData
fmt.Printf("%#v\n", rest) // DataModel{ID:"3", Value:"extra", Other:true, AnyID:0}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions