Struct does not implement interface if it has a method whose parameter implements interface
I have a package in which I have two interfaces
package main
type A interface {
Close()
}
type B interface {
Connect() (A, error)
}
I have also two structures which implements these interfaces
type C struct {
}
func (c *C) Close() {
}
type D struct {
}
func (d *D) Connect() (*C, error) {
c := new(C)
return c, nil
}
Next I have a function which as a parameter wants an object which implements interface B
func test(b B) {
}
Finally, at the main() function I create D structure object and want to call test() function
func main() {
d := new(D)
test(d)
}
If I try to build that package I have an error.
cannot use d (type *D) as type B in argument to test: *D does not implement B (wrong type for Connect method) have Connect() (*C, error) want Connect() (A, error)
It is simple example of my code where I use external package and want to mock structures for tests. Is it any solution to use interfaces instead of types?
Solution 1:
For implementing the interface there is one to concern about which is:
A Go type satisfies an interface by implementing the methods of that interface, nothing more. This property allows interfaces to be defined and used without having to modify existing code. It enables a kind of structural typing that promotes separation of concerns and improves code re-use, and makes it easier to build on patterns that emerge as the code develops.
The error you are getting because the struct D
you are using as an argument to test function does not implement the interface. The reason behind this is that the function Connect
you are using with receiver D
is different. Since it has different return type:
func (d *D) Connect() (*C, error) { // the struct D does not implement the interface B because of wrong function definition to interface B function
c := new(C)
return c, nil
}
while if you want to implement the interface B
the function definition along with its return type should match the function in interface B
which is
type B interface {
Connect() (A, error)
}
So if you want to implement the interface the Connect method you are using should match the Connect method of the interface B
package main
type A interface {
Close()
}
type B interface {
Connect() (A, error)
}
type C struct {
}
func (c *C) Close() {
}
type D struct {
}
func (d *D) Connect() (A, error) {
c := new(C)
return c, nil
}
func test(b B) {}
func main() {
d := new(D)
test(d)
}
Check on Go Playground
Consider this simple interface to represent an object that can compare itself with another value:
type Equaler interface {
Equal(Equaler) bool
}
and this type, T:
type T int
func (t T) Equal(u T) bool { return t == u } // does not satisfy Equaler
The argument type of T.Equal is T, not literally the required type Equaler.
In Go, the type system does not promote the argument of Equal; that is the programmer's responsibility, as illustrated by the type T2, which does implement Equaler:
type T2 int
func (t T2) Equal(u Equaler) bool { return t == u.(T2) } // satisfies Equaler
Solution 2:
The returned type for your Connect
method should be A
and not *C
.
The way you defined the Connect
method is that it should return an interface, not a specific type. You will still be able to return *C
as it implements the A
interface.
package main
type A interface {
Close()
}
type B interface {
Connect() (A, error)
}
type C struct {
}
func (c *C) Close() {
}
type D struct {
}
func (d *D) Connect() (A, error) {
c := new(C)
println("successfully created new C:", c)
return c, nil
}
func test(b B) {
b.Connect()
}
func main() {
d := new(D)
test(d)
}
Outputs
successfully created new C: 0xe28f0
Try it out yourself here