Given the current type parameter proposal (I don't expect it to change significantly) I want to write an interface constraint that limits struct types to those that embed another struct.
The idea is:
type Base struct {}
type Foo struct { Base }
type Bar struct { Base; s string }
type Baz struct { s string }
func fn[T HasBase](v T) {
// ...
}
The fn
function should be instantiated with Foo
and Bar
but not with Baz
. The purpose would be to access Base
(and its fields) from the function body, and possibly enforcing some particular type composition in API design.
I could try with the following:
type HasBase interface {
~struct{ Base }
}
where HasBase
is defined as the type set of types whose underlying type is struct{ Base }
.
Given that the underlying type of a struct literal is itself, this HasBase
allows Foo
but not Bar
.
I could add Bar
's underlying type to the constraint:
type HasBase interface {
~struct{ Base } | ~struct{ Base; s string }
}
But this is not scalable. I need to match the structs definitions literally, even the field names must match. Even worse, although the proposal hints that the above is possible, it was actually left out of the implementation.
So based on the linked issue I can use the following workaround, i.e. declare a method on the Base
type (possibly unexported) and throw that into the type constraint:
func (b Base) getBase() Base { return b }
type HasBase interface {
getBase() Base
}
which isn't much different than using HasBase
as a simple non-constraint interface (!!) and forgo generics — probably differing only in interface boxing of non-pointer types?
// also works, and also gives access to the getBase() method
func fn2(t HasBase) {
// ...
}
Am I missing something obvious, or is this actually the only way to do this as of today?
GoTip playground: https://gotipplay.golang.org/p/_SlZlnDL1dE