I was wondering why you can't do:
type Foo struct { A int }
type Bar Foo
foos := []Foo{Foo{1}, Foo{2}}
bars := []Bar(foos)
//cannot convert foos (type []Foo) to type []Bar
and I found out that this would require the runtime to perform a loop over the slice to convert each of the elements, which would be non-idiomatic Go. This makes sense.
However, could this not be solved by the compiler just aliasing Bar
as Foo
, so internally they're the same and they use the same type header underneath? I'm guessing the answer is no though I'm curious as to why.
This:
[]Bar(foos)
is a type conversion. Conversions have specific rules according to the spec:
A non-constant value x
can be converted to type T
in any of these cases:
x
is assignable to T
.
x
's type and T
have identical underlying types.
x
's type and T
are unnamed pointer types and their pointer base types have identical underlying types.
x
's type and T
are both integer or floating point types.
x
's type and T
are both complex types.
x
is an integer or a slice of bytes or runes and T
is a string type.
x
is a string and T
is a slice of bytes or runes.
None applies here. Why?
Because the underlying type of []Foo
is not the same as the underlying type of []Bar
. And a value of type []Foo
is not assignable to a variable of type []Bar
, see Assignability rules here.
The underlying type of Foo
is the same as the underlying type of Bar
, but the same does not apply to slices where the element type is Foo
and Bar
.
So the following works:
type Foo struct{ A int }
type Foos []Foo
type Bars Foos
func main() {
foos := []Foo{Foo{1}, Foo{2}}
bars := Bars(foos)
fmt.Println(bars)
}
Output (try it on the Go Playground):
[{1} {2}]
Note that since the actual memory representation of Foo
and Bar
is the same (because the underlying type of Bar
is Foo
), in this case using the package unsafe
you can "view" a value of []Foo
as a value of []Bar
:
type Foo struct{ A int }
type Bar Foo
func main() {
foos := []Foo{Foo{1}, Foo{2}}
bars := *(*[]Bar)(unsafe.Pointer(&foos))
fmt.Println(bars)
fmt.Printf("%T", bars)
}
This: *(*[]Bar)(unsafe.Pointer(&foos))
means that take the address of foos
, convert it to unsafe.Pointer
(according to spec all pointers can be converted to unsafe.Pointer
), then this Pointer
is converted to *[]Bar
(again according to the spec Pointer
can be converted to any other pointer type), and then this pointer is dereferenced (*
operator), so the result is a value of type []Bar
as can be seen in the output.
Output (try it on the Go Playground):
[{1} {2}]
[]main.Bar
Notes:
Quoting the package doc of unsafe
:
Package unsafe contains operations that step around the type safety of Go programs.
Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines.
What does this mean? It means that you shouldn't revert to using package usafe
every time it makes your life easier. You should only use it in exceptional cases, when not using it would make your program really slow and complicated.
In your program this is not the case as I proposed a working example with just a little refactoring (Foos
and Bars
being slices).
unsafe
steps around the type safety of Go. What does this mean? If you would change the type of foos
(e.g. drastically like foos := "trap!"
), your program would still compile and run, but most likely runtime panic would occur. Using usafe
you lose type checks of the compiler.
While if you use my other proposal (Foos
and Bars
), such changes/typos are detected at compile time.
As mentioned in "Why can I type alias functions and use them without casting?"
In Go, there is no such thing as a type alias.
The type
keyword introduces new named types. They are not aliases
If you compare two named types, the names must match in order for them to be interchangeable
This is what the spec mentions:
A type declaration binds an identifier, the type name, to a new type that has the same underlying type as an existing type, and operations defined for the existing type are also defined for the new type.
The new type is different from the existing type.