Type conversion for func type [duplicate] - go

When I define a custom type, it seems that the type of the underlying type makes a difference about whether I can pass it to a function as is or I need to convert it.
Question is:
Why does RuneFunc and StringMap work, but not Integer?
https://play.golang.org/p/buKNkrg5y-
package main
type RuneFunc func(rune) rune
type Integer int
type StringMap map[string]string
func main() {
//m := make(StringMap)
//mf(m)
var i Integer = 5
nf(i)
//var f func(rune) rune
//ff(f)
}
func mf(i map[string]string) {
}
func ff(i func(rune)rune) {
}
func nf(i int) {
}
Here, when I run this function called nf with Integer it complains although int is the underlying type. But when I call mf or ff they run successfully.

Integer and int
int and your new type Integer are 2 different, distinct types. Where Integer is expected, you have to pass a value of type Integer.
If you have an Integer value, you may use a simple type conversion to make it a value of type int, because the underlying type of Integer is int:
var i Integer = 5
nf(int(i))
What may be confusing and interesting at the same time is that you are allowed to pass an untyped constant without conversion:
nf(5)
Try these on the Go Playground.
The reason for this is in the Spec: Assignability:
A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:
[...]
x is an untyped constant representable by a value of type T.
5 is an untyped constant which is representable by a value of type int because the untyped constant 5 has a default type int, so it is representable by a value of type Integer (which has the same default type).
If you check the other assignability rules (not included in above quotation), none of them match the case where you attempt to pass a value of Integer for the parameter of type int, so that's not allowed.
See related question: Golang: Creating a Constant Type and Restricting the Type's Values
RuneFunc and func(rune) rune
The difference between this case and the previous one (Integer and int) is that int is a named type and func(rune) rune is not.
And there's an assignability rule which allows this:
x's type V and T have identical underlying types and at least one of V or T is not a named type.
So in this case:
var f RuneFunc
ff(f)
f is a named type, but the parameter type of ff() is func(rune) rune which is unnamed, so the assignment is allowed.

Go has a strict type system. Just because your type is merely an alias for int doesn't mean you can interchange the two freely, you'll still have to do a type conversion. Below is a working version of your main, here's the code on play ground: https://play.golang.org/p/BDTXnnG9Lg

Related

Why does %T not print the type of my constant?

I'm just learning golang using the official tour/tutorial. In one of the examples, I see a note that says An untyped constant takes the type needed by its context.
I'm trying this:
package main
import "fmt"
const (
// Create a huge number by shifting a 1 bit left 100 places.
// In other words, the binary number that is 1 followed by 100 zeroes.
Big = 1 << 100
)
func main() {
fmt.Printf("Big is of type %T\n", Big)
}
But this fails when I run it, with:
# command-line-arguments
./compile63.go:12:13: constant 1267650600228229401496703205376 overflows int
Why am I unable to discover the type of the constant this way? (Please note I'm a total noob and quite possibly haven't yet discovered enough about the language to be able to solve this myself).
func Printf
func Printf(format string, a ...interface{}) (n int, err error)
Printf formats according to a format specifier and writes to standard
output. It returns the number of bytes written and any write error
encountered.
The Go Programming Language
Specification
Variable
declarations
A variable declaration creates one or more variables, binds
corresponding identifiers to them, and gives each a type and an
initial value.
If a list of expressions is given, the variables are initialized with
the expressions following the rules for assignments. Otherwise, each
variable is initialized to its zero value.
If a type is present, each variable is given that type. Otherwise,
each variable is given the type of the corresponding initialization
value in the assignment. If that value is an untyped constant, it is
first converted to its default type; if it is an untyped boolean
value, it is first converted to type bool. The predeclared value nil
cannot be used to initialize a variable with no explicit type.
Constants
Constants may be typed or untyped. Literal constants, true, false,
iota, and certain constant expressions containing only untyped
constant operands are untyped.
A constant may be given a type explicitly by a constant declaration or
conversion, or implicitly when used in a variable declaration or an
assignment or as an operand in an expression. It is an error if the
constant value cannot be represented as a value of the respective
type.
An untyped constant has a default type which is the type to which the
constant is implicitly converted in contexts where a typed value is
required, for instance, in a short variable declaration such as i := 0
where there is no explicit type. The default type of an untyped
constant is bool, rune, int, float64, complex128 or string
respectively, depending on whether it is a boolean, rune, integer,
floating-point, complex, or string constant.
Numeric types
A numeric type represents sets of integer or floating-point values.
Some predeclared architecture-independent numeric types:
int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
The value of an n-bit integer is n bits wide and represented using
two's complement arithmetic.
There are also some predeclared numeric types with
implementation-specific sizes:
uint either 32 or 64 bits
int same size as uint
Big is an untyped constant. There is no type to discover. It is given a type when used in a variable or assignment. The default type for the untyped constant Big is int.
const Big = 1 << 100
In Go, all arguments are passed by value as if by assignment. For fmt.Printf, the second argument is of type interface{}. Therefore, equivalently,
var arg2 interface{} = Big // constant 1267650600228229401496703205376 overflows int
fmt.Printf("Big is of type %T\n", arg2)
The default type for an untyped integer constant is int. Overflow is a compile-time error.
For example,
package main
import "fmt"
const (
// Create a huge number by shifting a 1 bit left 100 places.
// In other words, the binary number that is 1 followed by 100 zeroes.
Big = 1 << 100
)
func main() {
var arg2 interface{} = Big
fmt.Printf("Big is of type %T\n", arg2)
fmt.Printf("Big is of type %T\n", Big)
}
Playground: https://play.golang.org/p/9tynPTek3wN
Output:
prog.go:12:6: constant 1267650600228229401496703205376 overflows int
prog.go:15:13: constant 1267650600228229401496703205376 overflows int
Reference: The Go Blog: Constants
While it is true that an untyped constant takes the type needed by its context, the type it can assume is limited by the primitives of the language, so a constant that big is not actually usable anywhere in the code, due to the fact that it does not fit even in an uint64. The only use it could have would be that of using it in another constant expression, because otherwise that error will always be thrown.
Note that in Printf (and similar functions), the constant is converted to an interface{}, and thus it takes a type of int by default. For 32-bit machines, you need to do a type conversion first if you have a constant expression which overflows int32.
const i = 1 << 50
fmt.Println(i) // => constant 1125899906842624 overflows int
fmt.Println(int64(i)) // => 1125899906842624
If you want to do proper arithmetic on arbitrarily big numbers, there is a handy package: math/big.
i := big.NewInt(1)
i.Lsh(i, 100)
fmt.Println(i.String())
Playground

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.

Custom type passed to function as a parameter

When I define a custom type, it seems that the type of the underlying type makes a difference about whether I can pass it to a function as is or I need to convert it.
Question is:
Why does RuneFunc and StringMap work, but not Integer?
https://play.golang.org/p/buKNkrg5y-
package main
type RuneFunc func(rune) rune
type Integer int
type StringMap map[string]string
func main() {
//m := make(StringMap)
//mf(m)
var i Integer = 5
nf(i)
//var f func(rune) rune
//ff(f)
}
func mf(i map[string]string) {
}
func ff(i func(rune)rune) {
}
func nf(i int) {
}
Here, when I run this function called nf with Integer it complains although int is the underlying type. But when I call mf or ff they run successfully.
Integer and int
int and your new type Integer are 2 different, distinct types. Where Integer is expected, you have to pass a value of type Integer.
If you have an Integer value, you may use a simple type conversion to make it a value of type int, because the underlying type of Integer is int:
var i Integer = 5
nf(int(i))
What may be confusing and interesting at the same time is that you are allowed to pass an untyped constant without conversion:
nf(5)
Try these on the Go Playground.
The reason for this is in the Spec: Assignability:
A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:
[...]
x is an untyped constant representable by a value of type T.
5 is an untyped constant which is representable by a value of type int because the untyped constant 5 has a default type int, so it is representable by a value of type Integer (which has the same default type).
If you check the other assignability rules (not included in above quotation), none of them match the case where you attempt to pass a value of Integer for the parameter of type int, so that's not allowed.
See related question: Golang: Creating a Constant Type and Restricting the Type's Values
RuneFunc and func(rune) rune
The difference between this case and the previous one (Integer and int) is that int is a named type and func(rune) rune is not.
And there's an assignability rule which allows this:
x's type V and T have identical underlying types and at least one of V or T is not a named type.
So in this case:
var f RuneFunc
ff(f)
f is a named type, but the parameter type of ff() is func(rune) rune which is unnamed, so the assignment is allowed.
Go has a strict type system. Just because your type is merely an alias for int doesn't mean you can interchange the two freely, you'll still have to do a type conversion. Below is a working version of your main, here's the code on play ground: https://play.golang.org/p/BDTXnnG9Lg

convert function type in Golang

// Each type have Error() string method.
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
// type error interface {
// Error() string
// }
func (f binFunc) Error() string {
return "binFunc error"
}
func func_type_convert() {
var err error
err = binFunc(add)
fmt.Println(err)
fmt.Println(i)
}
I have two questions about the code above:
I don't know why the Error method executed, when add function was converted into binFunc type?
Why the add function converted result was able to assign to an err error interface variable?
error is an interface:
type error interface {
Error() string
}
This means that any type which has a method: Error() string fulfills the interface and can be assigned to a variable of type error.
binFunc has such a method:
func (f binFunc) Error() string {
return "binFunc error"
}
New developers in Go sometimes find this confusing because they don't realize it's possible to attach methods to more than just structs. In this case binFunc is defined liked this:
type binFunc func(int, int) int
So the way this works is you are allowed to convert any function which has the same signature: (from the spec)
A function type denotes the set of all functions with the same parameter and result types.
So if you create a function add:
func add(x, y int) int {
return x + y
}
You are allowed to convert this into a binFunc:
binFunc(add)
And because of the Error method on binFunc we defined above, we are then able to assign this new binFunc to a variable of type error:
var err error
var bf binFunc = binFunc(add)
err = bf
fmt.Println's behavior is to call .Error() on errors for you:
If an operand implements the error interface, the Error method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
So to answer your questions:
The Error method is executed because fmt.Println looks for arguments of type error, invokes .Error() and prints the resulting string.
You are allowed to assign binFuncs to err because binFunc has an Error method. You cannot assign add directly to err because it does not have an Error method. But you are allowed to convert add to a binFunc because they have the same function signature, and by doing so you can then assign it to the err variable.
go spec dependencies:
type casting or conversion -> assignability -> type identity
explicit type casting or conversion
binFunc and func(int, int) int have same underlying representation.
binFunc(add)
note, type casting can happen between 2 types that have the same underlying representation. However, their type can be totally different.
type MyInt int
func main() {
var b MyInt = 3
a := int(b)
fmt.Println(a, b)
}
variable assignment
check type identity
based on type identity rule, binFunc is identical to func(int, int) int. So you can do type casting as below:
A named type is always different from any other type. Otherwise, two types are identical if their underlying type literals are structurally equivalent; that is, they have the same literal structure and corresponding components have identical types.
func(int, int) int
is type literal, and it's unnamed.
type binFunc func(int, int) int
is a named type.
Predeclared types, defined types, and type parameters are called named types. An alias denotes a named type if the type given in the alias declaration is a named type.
named type is different from others. but here, binFunc is compared with the un-named type: their underlying type literals are structurally equivalent, both func(int, int) int.
var bfunc binFunc = add
check assignability
variable of named type can be assigned with a value of unnamed type providing their underlying type is identical.
You may call this an implicit type conversion here, but it's not accurate. At least golang doesn't call this type conversion because the underlying type/representation is the same.
inspired by this answer
extra words
type assertion only requires type identity. Therefore, the rule is simpler than type assignability.

What is the difference between type conversion and type assertion?

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.

Resources