What is the difference between type conversion and type assertion? - go

What is the main differences between :
v = t.(aType) // type assertion
v = aType(t) // type conversion
Where should I use type assertion or type conversion ?

A type assertion asserts that t (an interface type) actually is a aType and t will be an aType; namely the one wrapped in the t interface. E.g. if you know that your var reader io.Reader actually is a *bytes.Buffer you can do var br *bytes.Buffer = reader.(*bytes.Buffer).
A type conversion converts one (non-interface) type to another, e.g. a var x uint8 to and int64 like var id int64 = int64(x).
Rule of thumb: If you have to wrap your concrete type into an interface and want your concrete type back, use a type assertion (or type switch). If you need to convert one concrete type to an other, use a type conversion.

tl;dr x.(T) asserts that the dynamic value of interface x is T at run time; T(x) converts the type of an expression x to some other type.
Type Assertion
You know that in Go an interface is basically a method set specification, and you can assign to an interface variable any value whose type implements that method set1.
The type assertion written x.(T) asserts that the value stored in the interface x is of type T. You use a type assertion when you want to unbox that value.
One of the most common uses is when you have interface{} and you need to retrieve the concrete value it stores. A typical example, Context values:
func foo(ctx context.Context) {
s := ctx.Value("my_key").(string) // signature is `Value(key interface{}) interface{}`
// do something with s...
}
It is called assertion because at compile time it is not known whether x actually holds the concrete type T, but you assert that it does. That's why the unchecked assertion y := x.(T) panics if x doesn't actually hold a T — you must use the comma-ok assignment v, ok := x.(T) to avoid it.
ctx = context.WithValue(ctx, "my_key", "foo")
s := ctx.Value("my_key").(int) // panic
v, ok := ctx.Value("my_key").(string)
fmt.Println(v, ok) // "foo" true
In addition, when T in x.(T) is an interface itself, the assertion checks that the value stored in x implements T. The outcome is the same as above.
Type Conversion
A type conversion written as T(x) instead "changes the type of an expression to the type specified by the conversion", i.e. changes the type of x to T. An important property of conversions is that they are statically checked2. An invalid conversion simply won't compile:
type Foo string
type Bar int
a := "foo"
fmt.Println(Bar(a)) // cannot convert a (type string) to type Bar
The main condition for a conversion to be valid is assignability between the types involved, but there's several more, including conversions between numerical types, strings and byte/rune slices, directed channels, slices and array pointers, etc.
In simple terms, you use a conversion when you already know what are the types involved, and simply want to change one to the other:
b := []byte("foo") // converts string literal to byte slice
Notes:
1: more formally, when the value's method set is a superset of the interface method set; this is also why the empty interface interface{} can hold any value, because any set is a superset of an empty set.
2: type assertions are also checked at compile time when the type T in x.(T) does not implement the interface. In practice, this won't help you catch errors when x is interface{} since all types implement it.

Related

How can I return a subtype of the specified return value (in this case interface{})?

I have an interface that defines one parameter to have type func(interface{}, proto.Message) interface{} and I'm trying to pass something of type func reduceMsg(a interface{}, b proto.Message) []*PersistentData to it. This results in the following compiler error:
Cannot use reduceMsg (type func(a interface{}, b proto.Message) []*PersistentData as type func(interface{}, proto.Message) interface{}
What is the reason for this error, and how can I work around it? It seems like returning a more specific type than interface{} should be legal. Here's a simple complete example that illustrates the issue:
package main
import "fmt"
func main() {
var t func() interface{} = func() []string { return []string{} }
fmt.Println(t)
}
The type of the object is the whole function signature. If the signature don't match, then it's not the same type and can't be assigned that way.
Anything can be assigned to the empty interface, because all types satisfy the interface, but in your problem neither type is the empty interface, you just have a function that returns an empty interface.
Not because a part of the function can be assigned to another it makes it the same. The type is the whole function signature. I think it's the same logic behind not being able to assign an int to an int8. You can cast them if you want, but for go, they are separate types and you need to deal with making the necessary conversions to be able to assign them.
What you can do is change your second function signature to return an empty interface like this:
func(interface{}, proto.Message) interface{}
func reduceMsg(a interface{}, b proto.Message) interface{} {
var a []*PersistentData
// do something here
return a
}
This way the function signature is the same, so it's consider the same type and you are returning an []*PersistentData. Of course you will need to do a type assertion before using it as such because the program will treat it as an {}interface because that is the type that the function returned.
Referencing the spec,
In assignments, each value must be assignable to the type of the operand to which it is assigned, with the following special cases:
Any typed value may be assigned to the blank identifier.
If an untyped constant is assigned to a variable of interface type or the blank identifier, the constant is first converted to its default type.
If an untyped boolean value is assigned to a variable of interface type or the blank identifier, it is first converted to type bool.
Assignability
A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:
x's type is identical to T.
x's type V and T have identical underlying types and at least one of V or T is not a named type.
T is an interface type and x implements T.
x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type.
x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
x is an untyped constant representable by a value of type T.
In general, Go doesn't allow you to implicitly convert values from one type to another, with the exception of being able to use concrete-typed objects as though they were interfaces (that they implement).
In this particular case, since your function doesn't actually return an interface{}, the compiler would have to do some extra work to wrap up the return value as an interface{} and return it; if you really want to accomplish what you're trying you can do this explicitly yourself:
type Foo struct {
X int
}
func create(x int) Foo {
return Foo{X: x}
}
func main() {
var f func(int) interface{} = func(x int) interface{} {
return create(x)
}
}
which is basically doing (explicitly) the wrapping operation that you want the runtime to do implicitly.

Go type assert nil to pointer type [duplicate]

This question already has an answer here:
Convert nil interface to pointer of something in Golang?
(1 answer)
Closed 7 years ago.
Why can't I type-assert a nil to a pointer type? What is the logic behind this?
package main
func main() {
var s interface{} = nil
var p *string = nil
var q *string = s.(*string)
_ = q
_ = p
}
You can't type assert something with no type.
The static type (or just type) of a variable is the type given in its
declaration, the type provided in the new call or composite literal,
or the type of an element of a structured variable. Variables of
interface type also have a distinct dynamic type, which is the
concrete type of the value assigned to the variable at run time
(unless the value is the predeclared identifier nil, which has no
type). The dynamic type may vary during execution but values stored in
interface variables are always assignable to the static type of the
variable.
Straight from the spec (emphasis mine)
Interfaces know the type of the underlying value. For instance if I have a interface with a type MyType it can't be type asserted to *string either. You could perhaps convert its type with some work, but type assertion and type conversion are different.
Also take a look here
For an expression x of interface type and a type T, the primary
expression
x.(T)
asserts that x is not nil and that the value stored in x is of
type T. The notation x.(T)

In golang why I can't use the struct as nested struct type?

The following code (play):
func main() {
buf := bytes.NewBuffer(make([]byte, 0))
rw := bufio.NewReadWriter(bufio.NewReader(buf), bufio.NewWriter(buf))
var r *bufio.Writer
r = rw
}
Gives the following compile-time error:
cannot use rw (type *bufio.ReadWriter) as type *bufio.Writer in assignment
What I expected is use a struct as a nested struct type. But if I declare r as io.Reader, this will be ok, so should I move to interface?
bufio.NewReadWriter() returns a concrete type, a pointer to a struct and bufio.Writer is also a concrete type, a struct. Neither *ReadWriter and *bufio.Writer is an interface!
In Go there is no automatic type conversion, you cannot assign a value of different concrete type to a variable.
You have 2 options:
Since bufio.ReadWriter embeds *bufio.Writer, you can simply refer to it and use that in the assignment:
var r *bufio.Writer
r = rw.Writer
Or you can declare r to be an io.Writer (it is an interface type) so that you can assign rw to it because rw implements io.Writer:
var r io.Writer
r = rw
Although I don't think creating r in this case is particularly useful because whenever you would use r you could also use rw.
Check out Go spec: Assignability:
A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:
x's type is identical to T.
x's type V and T have identical underlying types and at least one of V or T is not a named type.
T is an interface type and x implements T.
x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type.
x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
x is an untyped constant representable by a value of type T.
None of the cases apply to your code, so it is an invalid assignment.
When r is declared to be io.Writer, it is the the following case and therefore it is valid:
T is an interface type and x implements T.
Different type can't assign, GO do not support extension.

Go: Variable to a function, returning an interface

In Go, why can I not have a variable to a function, which returns an interface type?
Here's a minimal test case:
type DummyInterface interface {
Method(string) string
}
// Dummy implements the DummyInterface interface
type Dummy struct{}
func (d Dummy) Method(i string) string {
return i
}
// DummyFunc returns a Dummy pointer (which implements the DummyInterface interface)
var DummyFunc (func() *Dummy) = func() *Dummy {
a := Dummy{}
return &a
}
// DummyInterfaceFunc is declared as returning function returning an object which implements DummyInterface -- it
// is set to DummyFunc, which does return a conforming object
var DummyInterfaceFunc (func() DummyInterface) = DummyFunc
This fails to compile (Playground example here), stating:
cannot use DummyFunc (type func() *Dummy) as type func() DummyInterface in assignment
Yet, as you can see, a *Dummy does implement DummyInterface.
Why is this?
Because *Dummy is not the same type as DummyInterface. The rule that you can assign an object to something of an interface type that object implements only applies in that literal case. If the interface type appears in one of the parameters of a type (i.e. the return type of a function), assignment is not possible.
Refer to the assignability rules for more information.
Assignability
A value x is assignable to a variable of type T ("x is assignable to
T") in any of these cases:
x's type is identical to T.
x's type V and T have identical underlying types and at least one of V or T is not a named type.
T is an interface type and x implements T.
x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not
a named type.
x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
x is an untyped constant representable by a value of type T.
This is a similar concept to: Can I convert a []T to an []interface{}?
Basically func() *Dummy is a different type from func() DummyInterface, and conversion between them is not possible.
You will need to use the interface throughout to make the function signature the same.

How to convert value type by another value's reflect.Type in Golang

How to convert value type by another value's reflect.Type in Golang
maybe like this:
func Scan(value interface{}, b string) error {
converted := value.(reflect.TypeOf(b)) // do as "value.(string)"
return nil
}
How can do this properly in golang?
The only way to get a typed value out of an interface is to use a type assertion, and the syntax is value.(T) where T is a type. There's a good reason for this, because it makes the type of the type assertion expression computable: value.(T) has type T. If instead, you allowed value.(E) where E is some expression that evaluates to a reflect.Type (which I think is the gist of your question), then the compiler has no way to (in general) statically determine the type of value.(E) since it depends on the result of an arbitrary computation.

Resources