-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
Proposal / Rational
One of the uses for interfaces, is to provide a reduced-access abstraction, ensuring that a library is used correctly by a caller.
It is not uncommon to have similar interfaces for different classes of users; or, more generally, to have a single implementation for multiple interfaces.
This allows:
- Information hiding
- Interface simplification (from the caller's point of view)
It would be useful to implement a method once, and allow it to implement multiple similar interfaces.
Simple Example
Interface (for this example, the 'subtype')
type ExampleInterface interface {
// return type covariance
Clone() ExampleInterface
// parameter type covariance
SetParent(*ExampleStruct)
}
Struct (for this example, the 'supertype')
type ExampleStruct struct {
...
}
Implementation
// return type covariance (this implementation's return type is a supertype of the interface method's return type)
func (s *ExampleStruct) Clone() *ExampleStruct {
...
}
// parameter type covariance (this implementation's parameter type is a subtype of the interface method's parameter type)
func (s *ExampleStruct) SetParent(newParent ExampleInterface){
...
}
In this example, a struct and an interface are used as the supertype and subtype, but it should be equivalently valid for both the supertype and subtype to be interfaces.
Advanced Example
See Covariance of Interface Method Parameters & Return Values - Advanced Example
Implementation Details
-
At compile-time, it must be determined which interfaces are sub-interface of other interfaces. (or more precisely, which interfaces are not super-interfaces of other interfaces)
This is non-trivial. As go's syntax does not specify the subtype/supertype hierarchy, loops are possible.
(i.e. -A implements B
if and only ifC implements D
if and only ifA implements B
...) -
Syntactic sugar:
Allow super-interfaces to override embedded interfaces' methods, so long as the super-method is covariantly reducible to the embedded interface's method.
As an example:Method(interfaceType)implementationStruct
could overrideMethod(implementationStruct)interfaceType
, because all information required by the embedded interface is there.
Other Considerations
- This change would be GO 1 compatible.
- What about slice/array/map types?
Is it possible to accept[]implementationStruct
/[]*implementationStruct
in lieu of[]interfaceType
?
It would be helpful to have automatic conversion of arrays/slices/maps of supertypes to arrays/slices/maps of subtypes.
This appears to be related, but may or may not be possible, and is ultimately a separate issue.
See proposal [WIP]: interface-slices #30391