Currently, for some structs that contain fields that use interface{}, we've had to implement DeepCopyInto via a json marshal -> unmarshal
DeepCopyInto does not return an error, so currently to be safe we panic on errors.
We should either:
figure out a way to reliably copy interface{} without using something that can error like marshal/unmarshalling
add a way to check if DeepCopyInto failed for structs using this technique rather than panicking
Attempts to come up with a good solution for copying interface{} without involving errors have come up short.
One attempted design was to create a pkg/utils/deepcopy.Interface
type Interface interface {
DeepCopyInterface()
}
and replace references to interface{} with deepcopy.Interface. However, this means that primitive/builtin types cannot be used as we cannot implement this interface for them. This is a non-starter for things like definitionv1.Reference.Parameters.
The other option then is to add a way to check if DeepCopyInto failed. One option is to have structs that need to use a fallible copy process have a deepCopyCanary error member, and set it to the error if their DeepCopyInto failed. For example:
type T1 struct {
Vals map[string]interface{} `json:"vals"`
deepCopyCanary error
}
func (in *T1) DeepCopyInto(out *T1) {
data, err := json.Marshal(&in)
if err != nil {
in.deepCopyCanary = err
return
}
if err := json.Unmarshal(data, &out); err != nil {
// important: deepCopyCanary set on out
out.deepCopyCanary = err
}
}
This gets tricky if you have:
type T2 struct {
T1
}
type T3 struct {
*T2
}
Deeply nested types with fallible DeepCopyInto implementations make it infeasible to implement DeepCopyInto this way.
One possible solution would be to be able to tag types with fallible DeepCopyIntos, or even autogenerate them:
Currently, for some structs that contain fields that use
interface{}
, we've had to implementDeepCopyInto
via a json marshal -> unmarshalDeepCopyInto
does not return an error, so currently to be safe we panic on errors.We should either:
interface{}
without using something that can error like marshal/unmarshallingDeepCopyInto
failed for structs using this technique rather than panickingAttempts to come up with a good solution for copying
interface{}
without involving errors have come up short. One attempted design was to create apkg/utils/deepcopy.Interface
and replace references to
interface{}
withdeepcopy.Interface
. However, this means that primitive/builtin types cannot be used as we cannot implement this interface for them. This is a non-starter for things likedefinitionv1.Reference.Parameters
.The other option then is to add a way to check if
DeepCopyInto
failed. One option is to have structs that need to use a fallible copy process have adeepCopyCanary
error member, and set it to the error if theirDeepCopyInto
failed. For example:This gets tricky if you have:
Deeply nested types with fallible
DeepCopyInto
implementations make it infeasible to implementDeepCopyInto
this way.One possible solution would be to be able to tag types with fallible
DeepCopyInto
s, or even autogenerate them:Probably the simplest way to integrate this with
T1
being nested would be to generateDeepCopyCanary
for everything through a package annotation:This would generate:
Then, to safely
DeepCopy
aT3
, you would call:Another possible implementation of
DeepCopyCanary
would wrapDeepCopyInto
and return(<val>, error)
, so instead you would call:The generated code for this would probably be a bit trickier, but it could be a nicer experience.