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"
Related
I am trying to write a type constraint for a Go program, that accepts "anything you can take len() of". But I can't really figure it out.
I want something like:
LenOf[m Measurable](m M) int {
return len(m)
}
I tried a few things. Fx. this naiive thing, which do compile, but doesn't work on all types (like fx []User):
type Measurable interface {
~string | []any | ~map[any]any
}
Then went on to something like, the below which not only makes the function signature for LenOf() extremely clunky, but also have clumsy to write on call sites (and still can't get it to compile)
type Measurable[K comparable, V any] interface {
~string | []V | ~map[K]V
}
Why? The builtin len is already "generic".
With that said, let's see why defining such a constraint is a bad idea. The Go spec has a paragraph — Length and capacity, that can help:
If the argument type is a type parameter P, the call len(e) (or cap(e) respectively) must be valid for each type in P's type set. The result is the length (or capacity, respectively) of the argument whose type corresponds to the type argument with which P was instantiated.
The issues with writing an all-encompassing constraint for "measurable" types are:
it includes arrays [N]T, where the array length is part of the type, so your constraint would have to specify all possible arrays you want to capture
it includes array pointers *[N]T, which you can't easily abstract in a type constraint
it includes maps, which forces you to capture keys K and values V, which may or may not be the same as T. Plus, K must implement comparable.
So you'd have to write something like:
type Measurable[T any, K comparable, V any] interface {
~string | ~[]T | ~map[K]V | ~chan T
}
which notably doesn't include arrays, and doesn't distinctly capture pointer literals, e.g. to match []*int, you would have to instantiate T with *int.
You might simplify V away:
type Measurable[T any, K comparable] interface {
~string | ~[]T | ~map[K]T | ~chan T
}
The function LenOf then becomes:
func LenOf[T any, K comparable, M Measurable[T, K]](m M) int {
return len(m)
}
but you still have to supply K, so you have to instantiate LenOf at call site with bogus types for the map key:
LenOf[string, int]("foo")
// ^ actually useless
and you can't take advantage of type inference with the argument either.
In conclusion: just use len. Design your generic functions to work with type literals that support length, or add to the constraint only those types that your functions are reasonably expected to handle.
I have two concrete types called CreationOperator and AnnihilationOperator and I want to define a new concrete type that represents a string of operators and a real coefficient that multiplies it.
I found natural to define an abstract type FermionicOperator, from which both CreationOperator and AnnihilationOperator inherit, i.e.
abstract type FermionicOperator end
struct CreationOperator <: FermionicOperator
...
end
struct AnnihilationOperator <: FermionicOperator
...
end
because I can define many functions with signatures of the type function(op1::FermionicOperator, op2::FermionicOperator) = ..., such as arithmetic operations (I am building an algebra system, so I have to define operations such as *, +, etc. on the operators).
Then I would go on and define a concrete type OperatorString
struct OperatorString
coef::Float64
ops::Vector{FermionicOperator}
end
However, according to the Julia manual, I believe that OperatorString is not ideal for performance, because the compiler does not know anything about FermionicOperator and thus functions involving OperatorString will be inefficient (and I will have many functions manipulating strings of operators).
I found the following solution, however I am not sure about its implication and if it really makes a difference.
Instead of defining FermionicOperator as an abstract type, I define it is as the Union of CreationOperator and AnnihilationOperator, i.e.
struct CreationOperator
...
end
struct AnnihilationOperator
...
end
FermionicOperator = Union{CreationOperator,AnnihilationOperator}
This would still allow functions of the form function(op1::FermionicOperator, op2::FermionicOperator) = ..., but at the same time, to my understanding, Union{CreationOperator,AnnihilationOperator} is a concrete type, such that OperatorString is well-defined and the compilers can optimize things if it's the case.
I am particularly in doubt because I also considered to use the built-in Expr struct to define my string of operators (actually it would be more general), whose field args is a vector with abstract-type elements: very similar to my first design attempt. However, while implementing arithmetic operations on Expr I had the feeling I was doing something "wrong" and I was better off defining my own types.
If your ops field is a vector that, in any given instance, is either all CreationOperators or all AnnihilationOperators, then the recommended solution is to use a parameterized struct.
abstract type FermionicOperator end
struct CreationOperator <: FermionicOperator
...
end
struct AnnihilationOperator <: FermionicOperator
...
end
struct OperatorString{T<:FermionicOperator}
coef::Float64
ops::Vector{T}
function OperatorString(coef::Float64, ops::Vector{T}) where {T<:FermionicOperator}
return new{T}(coef, ops)
end
end
If your ops field is a vector that, in any given instance, may be a mixture of CreationOperators and AnnihilationOperators, then you can use a Union. Because the union is small (2 types), your code will remain performant.
struct CreationOperator
value::Int
end
struct AnnihilationOperator
value::Int
end
const Fermionic = Union{CreationOperator, AnnihilationOperator}
struct OperatorString
coef::Float64
ops::Vector{Fermionic}
function OperatorString(coef::Float64, ops::Vector{Fermionic})
return new(coef, ops)
end
end
Although not shown, even with the Union approach, you may want to use the abstract type also -- just for future simplicity and flexibility in function dispatch. It is helpful in developing robust multidispatch-driven logic.
I made a two element Vector struct and I want to overload the + operator.
I made all my functions and methods take references, rather than values, and I want the + operator to work the same way.
impl Add for Vector {
fn add(&self, other: &Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
Depending on which variation I try, I either get lifetime problems or type mismatches. Specifically, the &self argument seems to not get treated as the right type.
I have seen examples with template arguments on impl as well as Add, but they just result in different errors.
I found How can an operator be overloaded for different RHS types and return values? but the code in the answer doesn't work even if I put a use std::ops::Mul; at the top.
I am using rustc 1.0.0-nightly (ed530d7a3 2015-01-16 22:41:16 +0000)
I won't accept "you only have two fields, why use a reference" as an answer; what if I wanted a 100 element struct? I will accept an answer that demonstrates that even with a large struct I should be passing by value, if that is the case (I don't think it is, though.) I am interested in knowing a good rule of thumb for struct size and passing by value vs struct, but that is not the current question.
You need to implement Add on &Vector rather than on Vector.
impl<'a, 'b> Add<&'b Vector> for &'a Vector {
type Output = Vector;
fn add(self, other: &'b Vector) -> Vector {
Vector {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
In its definition, Add::add always takes self by value. But references are types like any other1, so they can implement traits too. When a trait is implemented on a reference type, the type of self is a reference; the reference is passed by value. Normally, passing by value in Rust implies transferring ownership, but when references are passed by value, they're simply copied (or reborrowed/moved if it's a mutable reference), and that doesn't transfer ownership of the referent (because a reference doesn't own its referent in the first place). Considering all this, it makes sense for Add::add (and many other operators) to take self by value: if you need to take ownership of the operands, you can implement Add on structs/enums directly, and if you don't, you can implement Add on references.
Here, self is of type &'a Vector, because that's the type we're implementing Add on.
Note that I also specified the RHS type parameter with a different lifetime to emphasize the fact that the lifetimes of the two input parameters are unrelated.
1 Actually, reference types are special in that you can implement traits for references to types defined in your crate (i.e. if you're allowed to implement a trait for T, then you're also allowed to implement it for &T). &mut T and Box<T> have the same behavior, but that's not true in general for U<T> where U is not defined in the same crate.
If you want to support all scenarios, you must support all the combinations:
&T op U
T op &U
&T op &U
T op U
In rust proper, this was done through an internal macro.
Luckily, there is a rust crate, impl_ops, that also offers a macro to write that boilerplate for us: the crate offers the impl_op_ex! macro, which generates all the combinations.
Here is their sample:
#[macro_use] extern crate impl_ops;
use std::ops;
impl_op_ex!(+ |a: &DonkeyKong, b: &DonkeyKong| -> i32 { a.bananas + b.bananas });
fn main() {
let total_bananas = &DonkeyKong::new(2) + &DonkeyKong::new(4);
assert_eq!(6, total_bananas);
let total_bananas = &DonkeyKong::new(2) + DonkeyKong::new(4);
assert_eq!(6, total_bananas);
let total_bananas = DonkeyKong::new(2) + &DonkeyKong::new(4);
assert_eq!(6, total_bananas);
let total_bananas = DonkeyKong::new(2) + DonkeyKong::new(4);
assert_eq!(6, total_bananas);
}
Even better, they have a impl_op_ex_commutative! that'll also generate the operators with the parameters reversed if your operator happens to be commutative.
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.
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{}...