How are interface{} values compared if I have a type of map[interface{}]interface{} and I have another map which has map[string]interface{}.
How are the hashes evaluated if I have a
m := make(map[string]interface{}) and I execute m[m["key"]] i.e. I am passing an interface{} value for a map with string as key.
And vice versa case, that is :
m1 := make(map[interface{}]interface{}) and I execute m1[m["key"]]
Go interfaces are a 2 word value, and are compared as such. From the Language Specification section on Comparison Operators:
Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.
A value x of non-interface type X and a value t of interface type T are comparable when values of type X are comparable and X implements T. They are equal if t's dynamic type is identical to X and t's dynamic value is equal to x.
Only the first point is relevant in the case of the map[interface{}]T, since the key is always converted to an interface{} for comparison.
Related
I have tried to use generics with Go, but I don't really understand when we use any or comparable as type parameter. Can someone help to understand these?
It depends on what / how you want to use values of the parameter type. Constraints restrict what you can do with values of those types.
any is an alias for interface{} which allows any type. If a parameter can be of any type, that basically won't allow you to do anything with it because you have no guarantee what it will be.
The comparable constraints only allows types that are comparable, that is, the == and != operators are allowed to use on values of them. This is good if you want to use the type as a key in a map (maps require key types to be comparable), or if you you want to find an element in a slice, and you want to use the == operator to compare the elements to something.
As an example, let's write a generic map-get function:
func get[K comparable, V any](m map[K]V, key K) V {
return m[key]
}
The K key type must be comparable, else it cannot be used as the key type of some map (m[K]V in the example). V on the other hand shouldn't be constrained, the value type may be anything, and we're not doing anything with it (just returning a value of type V), so using any here is the best choice.
Another example, a slice-find function:
func find[V comparable](what V, s []V) int {
for i, v := range s {
if v == what {
return i
}
}
return -1
}
find() returns the index of the first occurrence of what in the slice s, and if it's not in the slice, returns -1. The type parameter V here must be comparable, else you couldn't write v == what, using V any would be a compile-time error. The constraint comparable ensures this find() function can only be instantiated with types (and called with values) where the == operator is defined and allowed.
The difference between comparable and any will change with Go 1.20 (Q1 2023) and (accepted) the proposal "56548: spec: allow basic interface types to instantiate comparable type parameters".
any will implement the comparable constraint (which it does not before Go 1.20).
Go 1.20-rc1 states:
Comparable types (such as ordinary interfaces) may now satisfy comparable constraints, even if the type arguments are not strictly comparable (comparison may panic at runtime).
This makes it possible to instantiate a type parameter constrained by comparable (e.g., a type parameter for a user-defined generic map key) with a non-strictly comparable type argument such as an interface type, or a composite type containing an interface type.
The principle is:
After substitution, each type argument must satisfy the constraint (instantiated, if necessary) of the corresponding type parameter. Otherwise instantiation fails.
With "satisfy" being:
A type T satisfies a constraint interface C if
T implements C; or
C can be written in the form interface{ comparable; E }, where E is a basic interface and T is comparable and implements E.
Example:
Currently, any does not implement the comparable constraint.
With the proposed change any will be permitted as type argument for comparable: comparable can be written as interface{ comparable; E } and thus the new rule applies, and any is spec-comparable and implements E (where E is the empty interface in this case).
Currently, the type parameter P in the type parameter list
[P interface{ comparable; fmt.Stringer }]
cannot be instantiated with the type S
type S struct {
data any
}
func (S) String() string
because S is not strictly comparable.
With the proposed change, S must only be spec-comparable (which it is) and implement fmt.Stringer (which it does).
("spec-comparable" are for types of comparable operands)
(as opposed to "strictly comparable", which is for the types in comparable, namely the set of (non-interface) types for which == and != are defined and for which these operations are guaranteed to not panic)
The implementation as started:
CL 453979: "cmd/compile: enable new comparable semantics by default"
CL 453978: "go/types, types2: make the new comparable semantics the default"
Researching the interface value in go - I found a great (maybe outdated) article by Russ Cox.
According to it:
The itable begins with some metadata about the types involved and then becomes a list of function pointers.
The implementation for this itable should be the one from src/runtime/runtime2.go:
type itab struct {
inter *interfacetype
_type *_type
hash uint32 // copy of _type.hash. Used for type switches.
_ [4]byte
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}
First confusing thing is - how is an array - variable sized?
Second, assuming that we have a function pointer at index 0 for a method that satisfies the interface, where could we store a second/third/... function pointer?
The compiled code and runtime access fun as if the field is declared fun [n]uintpr where n is the number of methods in the interface. The second method is stored at fun[1], the third at fun[2] and so on. The Go Language does not have a variable size array feature like this, but unsafe shenanigans can be used to simulate the feature.
Here's how itab is allocated:
m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.mhdr)-1)*goarch.PtrSize, 0, &memstats.other_sys))
The function persistentalloc allocates memory. The first argument to the function is the size to allocate. The expression inter.mhdr is the number of methods in the interface.
Here's code that creates a slice on the variable size array:
methods := (*[1 << 16]unsafe.Pointer)(unsafe.Pointer(&m.fun[0]))[:ni:ni]
The expression methods[i] refers to the same element as m.fun[i] in a hypothetical world where m.fun is a variable size array with length > i. Later code uses normal slice syntax with methods to access the variable size array m.fun.
Two kinds of conversion of the same constant to float64 return the same value, but when I try to convert these new values to int, the results are different.
...
const Big = 92233720368547758074444444
func needFloat(x float64) float64 {
return x
}
func main() {
fmt.Println(needFloat(Big))
fmt.Println(float64(Big))
fmt.Println(int(needFloat(Big)))
fmt.Println(int(float64(Big)))
}
I'd expect the two first Println return the same type of value
fmt.Println(needFloat(Big)) // 9.223372036854776e+25
fmt.Println(float64(Big)) // 9.223372036854776e+25
so when I convert them to int, I expect the same output, but:
fmt.Println(int(needFloat(Big))) // -2147483648
fmt.Println(int(float64(Big))) // constant 92233720368547758080000000 overflows int
If your real question is why one attempt to convert to int produces a compile-time error message, but the other produces a very negative integer, it's because one is a compile-time conversion, and the other is a runtime conversion. I think it helps in these cases to be explicit about what you are expecting, and what can be run and what can't. Here's a Go Playground version of your code, where the last conversion is commented out. The reason for commenting it out is of course that it doesn't compile.
As Adrian noted in a comment, Big is a constant, specifically an untyped one. As Uvelichitel answered, a constant x (of any type) can be converted to a new and different type T if and only if
x is representable by a value of type T.
(The quote part is from the section Uvelichitel linked, except that mine adds the inner link for the word "representable".)
The expression float64(Big) is an explicit type conversion, with a constant as its x, so the result is a float64-typed constant with the given value. So far, that's fine: now we have 92233720368547758074444444 as a float64. This chops off some of the digits: the actual internal representation is 92233720368547758080000000 (see variant with %f directives). The low digits, ...74444444, have been rounded to ...80000000. See the link for "representable" for why the rounding occurs.
The expression int(float64(Big)) is an outer explicit type conversion surrounding an inner explicit type conversion. We already know what the inner type conversion does: it produces the float64 constant 92233720368547758080000000.0. The outer conversion tries to represent this new value as int, but it does not fit, producing an error:
./prog.go:18:17: constant 92233720368547758080000000 overflows int
if the commented-out line is uncommented. Note again that the value has been rounded, due to the inner conversion.
On the other hand, needFloat(Big) is a function call. Calling the function assigns the untyped constant to its argument (a float64) and obtains its return value (the same float64, value 92233720368547758080000000.0. Printing that prints what you'd expect, given the default or explicit formatting directive. The returned value is not a constant.
Similarly, int(needFloat(Big)) calls needFloat, which returns the same float64 value—not a constant—as before. The int explicit type conversion tries to convert this value to int at runtime, rather than at compile time. For such conversions between numeric types, there is a list of three explicit rules at https://golang.org/ref/spec#Conversions, plus a final caveat. Here, rule 2 applies: any fractional part is discarded. But the caveat also applies:
In all non-constant conversions involving floating-point or complex values, if the result type cannot represent the value the conversion succeeds but the result value is implementation-dependent.
In other words, there is no runtime error, but the int value you get—which in this case was -2147483648, which is the smallest allowed 32-bit integer—is up to the implementation. This particular implementation chose to use this particular negative number as its result. Another implementation might choose some other number. (Interestingly, in the playground, if I convert directly to uint I get zero. If I convert to int, then to uint, I get the 0x80000000 I expected.)
Hence, the key difference in terms of whether you get an error is whether you do the conversion at compile time, via constants, or at runtime, via runtime conversion.
int(float64(Big)) //illegal because
A constant value x can be converted to type T if x is representable by
a value of T
int(needFloat(Big)) //is non-constant expression because of function call
A non-constant value x can be converted to type T in any of these
cases:
- x's type and T are both integer or floating point types.
https://golang.org/ref/spec#Conversions
User inputs an integer which corresponds to a value of defined enumerated type. I need to assign that value to variable t. This is what I thought of:
type test = (red,green,blue,fish);
var t:test;
n,i:integer;
begin
readln(n);
t:=red;
for i:=1 to n do
t:=succ(t);
end.
Am I overcomplicating the task? Is it possible to write a simpler alogrithm?
You should be able to just cast the integer to the enumerated type, for example:
t := test(n);
If you want to go the other way, then use ord:
n := ord(t);
That should let you move numerically to any item in the list. You can check the bounds with:
Ord(Low(test)))
and
Ord(High(test))
..where test is your type.
I've been trying to implement a function which can randomly select an element from any type of slice (like python's random.choice function)
func RandomChoice(a []interface{}, r *rand.Rand) interface{} {
i := r.Int()%len(a)
return a[i]
}
However, when I try to pass in a slice of type []float32 into the first argument this error occurs:
cannot use my_array (type []float32) as type []interface {} in function argument
is this a fundemental misuse of interface{}? is there some better way to do this?
Re: is there some better way to do this?
IMO there is. The OP approach is inefficient wrt to simple:
var v []T
...
// Select a random element of 'v'
e := v[r.Intn(len(v))]
...
Note that both of the approaches will panic for len(v) == 0 until a pre-check for this is made.
Using reflection:
func RandomChoice(slice interface{}, r *rand.Rand) interface{} {
x := reflect.ValueOf(slice)
return x.Index(r.Intn(x.Len())).Interface()
}
From the language specification:
Two types are either identical or different.
Two named types are identical if their type names originate in the
same TypeSpec. A named and an unnamed type are always different. Two
unnamed types are identical if the corresponding type literals are
identical, that is, if they have the same literal structure and
corresponding components have identical types. In detail:
Two array types are identical if they have identical element types and
the same array length.
Two slice types are identical if they have
identical element types.
Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical
types, and identical tags. Two anonymous fields are considered to have
the same name. Lower-case field names from different packages are
always different.
Two pointer types are identical if they have identical base types.
Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types
are identical, and either both functions are variadic or neither is.
Parameter and result names are not required to match.
Two interface types are identical if they have the same set of methods with the same names and identical function types. Lower-case
method names from different packages are always different. The order
of the methods is irrelevant.
Two map types are identical if they have identical key and value types.
Two channel types are identical if they have identical value types and the same direction.
And:
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.
Any value may be assigned to the blank identifier.
The combination of these two results that you can't assign a []MyType to an []interface{}.
Well, I can hardly believe after all the searching, that the first listed related question was: Type converting slices of interfaces in go which pretty much contains the answer.
Changing RandomChoice to use the InterfaceSlice function described in the answer to that question yields:
func RandomChoice(slice interface{}, r *rand.Rand) interface{} {
islice := InterfaceSlice(slice)
i := r.Int()%len(islice)
return islice[i]
}
although apparently this answer is not very well performing, because it requires the entire slice to be converted to []interface{}...