Explain type sets. How do unions and intersections combine, and which interfaces can only be used as constraints?
Question 116HardGo 1.22 to 1.25
Every interface denotes a type set:
- A method element gives the set of all types that have that method.
- A union
A | Bgives the union of the two sets. - Elements on separate lines are intersected.
A type satisfies the constraint if it is in the set.
type SignedInt interface{ ~int | ~int32 | ~int64 }
type Stringer interface{ String() string }
// Intersection: named int-like types that ALSO have String()
type PrintableInt interface {
SignedInt
Stringer
}
// Empty type set: legal to declare, but no type can satisfy it
type Impossible interface {
int
string
}
Restrictions worth knowing:
- An interface with type terms (
int,~int, a union) or withcomparableis a constraint interface. It cannot be the type of a variable, field or parameter.var n SignedIntfails with "cannot use type SignedInt outside a type constraint". - A union term cannot be an interface that has methods, and it cannot be
comparable. - Union terms (other than interfaces) must have disjoint type sets.
int | ~intis an error because~intalready includesint. - A type parameter cannot itself be a union term.
[T any, U T | int]is illegal.
What the interviewer wants to hear is that "an interface is a set of types" is the mental model, and that basic (method-only) interfaces are a special case.
More on Generics
- Q114What is a constraint in Go generics? Show the different ways to write one, including the
[P *C]parsing gotcha. - Q115What does the
~(tilde) token mean in a constraint? What happens without it? - Q117What is
comparable? What changed in Go 1.20, and what does this program print? - Q118Which operations can you perform on a value of type parameter type? Why can't you access a struct field even when every type in the set has it?
- Q119How does type inference work? When does it fail, and how do you order type parameters to make partial instantiation pleasant?
- Q120Can you store a generic function in a variable or pass it around without instantiating it?