-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Description
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 compileIt 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 5Destructuring 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 fooDestructuring 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}