Assertion of zero int to int32 - go

I noticed that Go assertion doesn't work as I expect for zero int. Here is the code:
var i interface{}
i = 0
i32, ok := i.(int32)
fmt.Println(ok)
fmt.Println(i32)
The output is following:
false
0
I can't find an explanation of this behavior. Is it a bug?

i doesn't contain an int32, it contains an int, and so the type assertion fails.
Assigning a literal value directly to an interface{} like this or using one in a short variable declaration can be a bit confusing because you don't really get to see what type of value you get; you need to know the rules for the "default type" of a constant. If this gives you trouble, you can explicitly cast it to a type, like
i = int32(0), which will ensure that i does in fact contain an int32.

No, it's not a bug, it's a well defined behavior.
This line:
i = 0
Is an assignment, and you use the untyped 0 integer constant to assign to i. Since a (concrete) type is needed to carry out the assignment (and is type being interface{} does not define one), the default type of that untyped constant will be used which is int. Read The Go Blog: Constants:
The answer is that an untyped constant has a default type, an implicit type that it transfers to a value if a type is needed where none is provided.
You can verify it if you modify it like this:
i2, ok := i.(int)
fmt.Println(ok)
fmt.Println(i2)
Which outputs:
true
0
If you would use a typed constant:
i = int32(0)
Then yes, the stored value would be of type int32, and you would get the same output:
i = int32(0)
i32, ok := i.(int32)
fmt.Println(ok)
fmt.Println(i32)
Try the examples on the Go Playground.

Related

Type conversion for func type [duplicate]

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

What the purpose of function-like initialization for builtin types in go?

I know, go converts right side untyped constant to left side typed variable right after := initialization in expression: a := 5.
And it looks like the same as b := int(5) statement. So, what is the purpose of the second statement and how it differs from the first one?
Also, somewhere, I have seen []int(nil) expression, that's a bit confuse me.
Generally you don't need to use := in combination with a type conversion because it defeats the point of :=. := is there to infer the type. The type can generally be inferred from a constant value.
But not always. For example...
package main
import (
"fmt"
)
func main() {
a := 5
b := uint(5)
var c uint = 5
fmt.Printf("%T\n", a)
fmt.Printf("%T\n", b)
fmt.Printf("%T\n", c)
}
a := 5 infers that a is of type int, but what if you wanted it to be something else? 5 could be a bunch of different integer types. Maybe an unsigned integer? Maybe an integer of a specific length like int16?
You can do this by either specifying the type of the constant explicitly, as in b := uint(8), or by specifying the type of the variable explicitly, as in var c uint = 8. With b := uint(8), b's type is inferred from the uint constant being assigned to it. With var c uint = 8, 8 is inferred to be an unsigned integer because it is being assigned to a variable with the type uint. In both cases, b and c will be of type uint.
I'm not aware of any significant difference between b := uint(8) and var b uint = 8, so which you use is largely up to taste. I have a slight preference for var b uint = 8 because it is visually distinct from b := 8 to make it clear explicit types are being used.
You are referring to a type conversion. A type conversion is used to convert a value of one type to a value of another type.
The expression int(5) converts the untyped 5 to an int with value 5.
The expression []int(nil) converts the untyped nil to a []int with value nil.
The statements
a := 5
b := int(5)
declare a int variable with value 5. The declaration of a uses the default type for integer constants (which is int). The declaration of b explicitly uses the int type. Otherwise, they are the same. The use of the default type in the declaration of a is usually preferred over the explicit type in the declaration of b. The type conversion is commonly used for types other than the default type. Example c := int64(22).

Golang not able to detect implicitly typed const when assigning to a variable

When I try to assign an implicitly typed constant to a variable, the assigned variable doesn't detect the custom type and instead gets assigned as the underlying primitive type. i.e;
For:
type Custom string
const (
First Custom = "10"
Second = "20"
)
If I have a function:
func SomeFunc( x Custom) {
fmt.Printf("Inside func %v %v", x, reflect.TypeOf(x))
}
Then when I:
out := Second
SomeFunc(out)
it errors with:
cannot use out (type string) as type Custom in argument to SomeFunc
However SomeFunc(Second) works fine.
Also
fmt.Printf("%v %v\n",reflect.TypeOf(second),reflect.TypeOf(out)) //prints string string
Here is the reproducer: https://play.golang.org/p/Iv-C1ee992
Can someone help me understand what is happening here?
Second is an untyped const and has this property (https://blog.golang.org/constants):
An untyped constant is just a value, one not yet given a defined type
that would force it to obey the strict rules that prevent combining
differently typed values.
...
Assigning them to a variable of any type compatible with strings works
without error.
On the contrary out is a variable of type string. Again from the blog post:
and by now you might be asking, "if the constant is untyped, how does
str get a type in this variable declaration?" The answer is that an
untyped constant has a default type, an implicit type that it
transfers to a value if a type is needed where none is provided. For
untyped string constants, that default type is obviously string

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

what's wrong with golang constant overflows uint64

userid := 12345
did := (userid & ^(0xFFFF << 48))
when compiling this code, I got:
./xxxx.go:511: constant -18446462598732840961 overflows int
Do you know what is the matter with this and how to solve it ?
Thanks.
^(0xFFFF << 48) is an untyped constant, which in go is an arbitrarily large value.
0xffff << 48 is 0xffff000000000000. When you negate it, you get -0xffff000000000001 (since with two's complement, -x = ^x + 1, or ^x = -(x + 1)).
When you write userid := 12345, userid gets the type int. Then when you try to and (&) it with the untyped constant -0xffff000000000001 the compiler figures that this constant needs to be an int. At this point, the compiler complains because the value is too large in magnitude to be an int.
If you're trying to get the constant 0x0000ffffffffffff, then you can use 1<<48 - 1, which (if you've got 64-bit ints), will fit. Since your code will never work if int is 32-bits, then you should probably use int64 in your code rather than int to make it portable.
The blog post https://blog.golang.org/constants explains how constants work, and some background on why they are the way they are.
The Go Programming Language Specification
Constants
Numeric constants represent values of arbitrary precision and do not
overflow.
Constants may be typed or 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
int is an implementation-specific size, either 32 or 64 bits.
userid is of type int. For example,
package main
import "fmt"
func main() {
userid := 12345
did := uint64(userid) & ^uint64(0xFFFF<<48)
fmt.Println(userid, did)
}
Output:
12345 12345

Resources