Skip to content

Instantly share code, notes, and snippets.

@JavierZunzunegui
Last active February 4, 2020 13:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JavierZunzunegui/1a1889a0f33450d68e175c1dc3a77419 to your computer and use it in GitHub Desktop.
Save JavierZunzunegui/1a1889a0f33450d68e175c1dc3a77419 to your computer and use it in GitHub Desktop.
go2 Relaxed Type Conversion Proposal

Relaxed Type Conversion Proposal

Motivation

While this is a standalone proposal, it is motivated by the go2 generics proposal (issue and doc). The connection between this proposal and generics is presented here (originally here).

Overview

In go, one can convert any type to another with the same type definition: type A int; var _ = A(int(1)) is perfectly valid (play).

This proposes an extension to this whereby slices, maps, arrays, channels and pointers with convertible inner types may also be convertible. This type of conversion is efficient as deep copies are never required (for example, for a slice only a reflect.SliceHeader is copied).

Details

The slice conversion type A int; var _ = []A([]int{1}) is not currently valid (see FAQ), but would be under this proposal (play, uses unsafe). The same can be done with maps (play), arrays (play), channels (play), and pointers (play).

All these have in common that they may result in the same memory being addressed by mutiple pointers of different types, i.e. a *A and a *int may point to the same address, which is not otherwise allowed in go. While this can obviously be done via unsafe as shown in the play links above, I put it for debate whether this can be added to the runtime safely with regards to gc, escape analysis, finalizers, etc. Note however the memory is always shared exclusively by types of equal size and which have either a common type declaration (in the traditional meaning of it) or a common 'relaxed' type declaration (in the meaning introduced by this proposal). This excludes converting []int to []int64, []string to [][]byte, etc - even though int-int64 and string-[]byte are convertible, these are exceptions and do not have common type declarations (int64 is not type int64 int and string is not type string []byte).

With regards to interfaces and structs, they follow the same proposed new rules (slices, maps, arrays, channels and pointers with interface or struct inner types may be converted to others with convertible inner types), though note the existing conversion restrictions on both interfaces and structs with regards to private methods/members still hold. Therefore type A interface {String() string}; type B interface {String() string}; var _ = []A([]B{}) would now be allowed (play), but not if A and B were on different packages and the String() string method were not exported (play). Similarly for structs (play), with an equivalent restriction on private members in different packages (play). Converting from non-interface to interface (i.e. []interface{}([]int{})) or to a non-equivalent interface (type I interface {Foo()}; []interface{}([]I{})) is not allowed.

This proposal does not change the conversion rules for func, i.e. type A int; var _ = (func(A))(func(int){}) is still illegal.

It might also be possible to allow conversion of structs to structs of different type definition, but differing only in the types of their members, and these being convertible in the context presented above. For example, given type A struct {I int}; type MyInt int; type B struct {I MyInt}, we could allow B(A{}). I am not certain this would add value under the generics proposal or indeed if it has additional technical difficulties, so I am leaving out of this proposal.

Uses

The original use of these changes is in the context of the generics proposal, discussed here (originally here). On its own, this has the advantage of limiting the need to perform deep copies unnecessarily: if an API requires a []MyInt for type MyInt int, a []int can be passed in without the need of copying. This however is not normally required as it is go standard to keep APIs to standard types whenever possible, meaning the real value of this proposal is closely tied to the generic one and should be discussed primarily in that context.

@JavierZunzunegui
Copy link
Author

JavierZunzunegui commented Oct 25, 2019

Please discuss this proposal here, and whatever applies to generics in particular in golang/go#15292 (comment).

@ianlancetaylor
Copy link

Seems like you should at least mention the FAQ entry https://golang.org/doc/faq#convert_slice_with_same_underlying_type .

@JavierZunzunegui
Copy link
Author

Thanks @ianlancetaylor - updated this gist and golang/go#15292 (comment) with a reference to the FAQ.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment