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 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}