Open luna-duclos opened 7 years ago
cc: @griesemer
This is something I've always found a little counterintuitive, especially if the underlying types are the same. I can think of a couple of times I've had to write my own strings.Join
routines because the alternative is to copy the entire slice. e.g., https://github.com/ericlagergren/decimal/blob/522450d1e655d86ef13b0bfad42d090a0240ce03/suite/suite.go#L73
It seems to make sense that if T1 and T2 are slices whose elements are all the same underlying types, they should be convertible without an allocation.
@luna-duclos Note that for struct field tags, conversion is ignoring the tags "all the way down" - not just one level deep. That would be something to consider if we were to make this change. (Though this is not going to happen for Go 1.0 I suspect).
This goes for non-struct types as wel. If you have this:
type MyType int
You can't do this:
a := []int{1, 2, 3}
b := []MyType(a)
Instead, you have to do this:
a := []int{1, 2, 3}
b := make([]MyType, len(a))
for i, v := range a {
b[i] = MyType(v)
}
Making b := []MyType(a)
possible would perhaps eliminate the desire for generics in some cases?
Seems related to #15209, which is about append
and copy
.
As I understand it, I think this proposal is asking for a concept that the language does not currently have: we permit converting []A
to []B
when A
and B
have identical underlying types ignoring field tags and type names, recursively (and this actually applies to all composite types, not just slices).
That can't really be the rule, though, because it breaks for interface types. Consider
type X int
type Y1 interface { M(X) }
type Y2 interface { M(int) }
var V1 []Y1
var V2 []Y2
Can I write V2 = ([]Y2)(V1)
? According to the rule I wrote above, I can, because the interface types are identical ignoring type names. But this conversion can't be right, because the set of types that implement Y1
is disjoint from the set of types that implement Y2
.
So the conversion rule must be something more complex than I wrote above. What is it?
It can be done by adding a type convertion check along with underlying type equality. If we formulate the rule like this: "permit converting composite types like []A to []B when A and B have identical underlying types ignoring field tags and type names and can be type converted, recursively"
Change https://golang.org/cl/105937 mentions this issue: reflect: prevent conversion of invariant slices
@vvoronin Thanks, but I'm not sure I understand your adjusted rule. Clearly it can't apply at top level, since at top level the types can not be converted. So, when does it apply?
@ianlancetaylor Oh, you are right, sorry for inconvenience, my english is quite bad.
I meant "when simple non-composite type can be type converted and have have identical memory layout, composite of that type also must be able to be type converted"
For example:
type A struct { A string }
type B struct { A string }
// we can convert this
var varA A = A(B{})
// and A and B have identical memory layout, so
var sliceOfB []B = []B([]A{})
type Y1 interface { M(X) }
type Y2 interface { M(int) }
var V1 Y1
var V2 Y2
// we cannot convert this
V2 = Y2(V1)
// so we cannot convert a slice or map of this
// we can convert this
byteArray := []byte(`string`)
// but memory layout is different
// so we cannot convert a slice or map of this
Currently, as of Go 1.8, one can convert between two identical types, A and B, defined as follows:
It is however not possible to convert between
[]A
and[]B
, even though the memory layout of both slices is identical, and one has to resort to following hackery to get both compile time checking and O(1) conversion time:This is often useful when different parts of an application are responsible for different parts of the handling of the lifetime of an object. For example, a database layer could return []A, which would be a type with
db:"foo"
decorators to help with database deserialization using the sqlx package, while the http handler wants to use []B, because it contains the field tags necessary for json serialization.Currently, a copy or unsafe is required, I propose allowing slices with identical memory layouts to be converted between another, and to restrict this to only 1 level deep to reduce possible complexity in the compiler.
This is a similar reasoning why the spec was changed to allow structs with different field tags to be converted between eachother, and I believe it would help avoid usage of unsafe and copies in many applications which seperate out concerns between different layers.