3

I have an interface which defines a method. I have a struct which implements this interface. In it, I have implemented the methods from this interface and also have additional methods defined.

For example:

package main

import (
    "fmt"
)   

type Animal interface {
    MakeNoise()
}

type Dog struct {
    color string
}

/* Interface implementation */

func (d *Dog) MakeNoise() {
    fmt.Println("Bark!")
}

/* End Interface implementation */

func (d *Dog) WagTail() {
    fmt.Println(d.color + " dog: Wag wag")
}

func NewDog(color string) Animal {
    return &Dog{color}
}

func main() {
    dog := NewDog("Brown")
    dog.MakeNoise()
    dog.WagTail()

}

On Playground: https://play.golang.org/p/B1GgoNToNl_l

Here, WagTail() is not part of the Animal interface but belongs to the Dog struct. Running this code gives an error

dog.WagTail undefined (type Animal has no field or method WagTail).

Is there a way I could have a struct adhere to an interface and also define it's own methods?

Himanshu
  • 12,071
  • 7
  • 46
  • 61
Bharat
  • 2,960
  • 2
  • 38
  • 57
  • Will that make Dog adhere to the Animal interface? How would the relationship between Dog and Animal be enforced ? – Bharat Aug 01 '18 at 01:10
  • So I'd need to do `var _ Animal = (*Dog)(nil)` ? According to comments in that thread, this is an unofficial way to enforce interface methods are implemented. This would work for my purpose. But wondering if there is a better way to do the scenario I have? Is my code example good go code? – Bharat Aug 01 '18 at 03:57
  • The code does compile. Please see the playground: https://play.golang.org/p/IRmreX4nhYt. – Bharat Aug 01 '18 at 04:06

4 Answers4

4

This may help you.

d := dog.(*Dog)
d.WagTail()

On Playground: https://play.golang.org/p/KlNqpmvFTJi

Himanshu
  • 12,071
  • 7
  • 46
  • 61
shal
  • 66
  • 2
  • This seems useful. So this is casting the Animal to be a Dog right? – Bharat Aug 01 '18 at 15:04
  • @Bharat no this is not casting an `Animal` interface to be a `Dog`. This is just using type assertion to get the underlying value of Dog. But creating a new instance for struct and then using it to call the methods is the same like creating an instance of Dog and using it directly rather than creating an instance of struct from underlying value of `Animal` interface – Himanshu Aug 01 '18 at 15:22
  • Thanks for the explanation. `d := dog.(*Dog)` is equivalent to `d := &Dog{}` right? – Bharat Aug 01 '18 at 15:35
  • 1
    @Bharat that's right both are creating an instance of struct Dog which will be used for calling the methods with Dog struct as receiver. – Himanshu Aug 01 '18 at 16:54
  • @Bharat,@Himanshu I don't think `d := dog.(*Dog)` will create an instance of struct Dog. `d` and `dog` are point to the same address. On Playground: https://play.golang.org/p/eWBawUhhy22 – shal Aug 02 '18 at 06:11
  • @shal `d := dog.(*Dog)` this is using type Assertion for Animal interface which is pointing to the same address. In Golang you create a variable using `:=` operator which is called short variable declaration. – Himanshu Aug 02 '18 at 14:58
2

You can definitely do it, one such method is using type assertion as shown in the other answer here. Otherwise the answer by @Himanshu here describes the situation very well.

I would like to add to the discussion to further describe how you

could have a struct adhere to an interface and also define it's own methods

The MakeDog method returns an Animal, and there are a number of reasons you may consider returning a Dog (or whichever concrete type) directly.

The reason I bring this up is because of something someone told me about creating methods when I first started programming in Go:

Accept an Interface and return a Concrete type (such as a Struct)

Interfaces can accept any concrete type. That's why they are used when you don't know the type of argument you are passing to the function.

I did a google search with the following terms and quite a few articles turned up

golang accept interface, return struct

For example: https://mycodesmells.com/post/accept-interfaces-return-struct-in-go and http://idiomaticgo.com/post/best-practice/accept-interfaces-return-structs/

I have put together a little bit of a demo extending on your concepts in your question to try and clearly describe interface methods as well as methods and attributes for specific types

Taken from this snippet on Playground

package main

import (
    "fmt"
)

type Animal interface {
    MakeNoise() string
}

// printNoise accepts any animal and prints it's noise
func printNoise(a Animal) {
    fmt.Println(a.MakeNoise())
}

type pet struct {
    nLegs int
    color string
}

// describe is available to all types of Pet, but not for an animal
func (p pet) describe() string {
    return fmt.Sprintf(`My colour is "%s", and I have "%d" legs`, p.color, p.nLegs)
}

type Dog struct {
    pet
    favouriteToy string
}

// MakeNoise implements the Animal interface for type Dog
func (Dog) MakeNoise() string {
    return "Bark!"
}

// WagTail is something only a Dog can do
func (d Dog) WagTail() {
    fmt.Println("I am a dog,", d.pet.describe(), ": Wags Tail")
}

type Cat struct {
    pet
    favouriteSleepingPlace string
}

// MakeNoise implements the Animal interface for type Cat
func (Cat) MakeNoise() string {
    return "Meow!"
}

// ArchBack is something only a Cat can do
func (c Cat) ArchBack() {
    fmt.Println("I am a cat,", c.pet.describe(), ": Arches Back")
}

type Bird struct {
    pet
    favoritePerch string
}

// MakeNoise implements the Animal interface for type Cat
func (Bird) MakeNoise() string {
    return "Tweet!"
}

// Hop is something only a Bird can do
func (c Bird) Hop() {
    fmt.Println("I am a bird,", c.pet.describe(), ": Hops to a different perch")
}

func main() {
    dog := Dog{
        pet:          pet{nLegs: 4, color: "Brown"},
        favouriteToy: "Ball",
    }
    printNoise(dog)
    dog.WagTail()

    cat := Cat{
        pet: pet{nLegs: 4, color: "Tabby"},
        favouriteSleepingPlace: "Sofa",
    }
    printNoise(cat)
    cat.ArchBack()

    bird := Bird{
        pet:           pet{nLegs: 2, color: "Rainbow"},
        favoritePerch: "Back of Cage",
    }

    printNoise(bird)
    bird.Hop()

}
edwardsmatt
  • 2,034
  • 16
  • 18
  • `type assertion and/or type conversion` are way too different they are not same. Please check more information for type assertion. Go does not works like java. – Himanshu Aug 01 '18 at 09:04
  • And OP didnot require to type assert the interface. OP wants to implement the method using same struct method receiver. – Himanshu Aug 01 '18 at 09:06
  • @Himanshu the intention of my answer was to further describe how the OP `could have a struct adhere to an interface and also define it's own methods`. I clarified the section which appears to have caught your attention. – edwardsmatt Aug 01 '18 at 09:26
2

The error described it all:

dog.WagTail undefined (type Animal has no field or method WagTail)

To implement an interface you should implement all methods defined inside it.

dog := NewDog("Brown")
dog.MakeNoise()
dog.WagTail()

Now NewDog returns Animal interface which contains MakeNoise method but not WagTail.

The only way to manage your requirement is either create a variable of struct type Dog and then you can call any method having Dog as receiver.

d := &Dog{"Brown"}
d.WagTail()

Or you can return the pointer to Dog struct from NewDog method just like you did in your code mentioned in the comment as:

func NewDog(color string) *Dog {
    return &Dog{color}
}

But if the method is not defined in interface you cannot implement it using the struct as method receiver.

Golang provides a way in which:

You can ask the compiler to check that the type T implements the interface I by attempting an assignment using the zero value for T or pointer to T, as appropriate

type T struct{}
var _ I = T{}       // Verify that T implements I.
var _ I = (*T)(nil) // Verify that *T implements I.

If T (or *T, accordingly) doesn't implement I, the mistake will be caught at compile time.

If you wish the users of an interface to explicitly declare that they implement it, you can add a method with a descriptive name to the interface's method set. For example:

type Fooer interface {
    Foo()
    ImplementsFooer()
}

A type must then implement the ImplementsFooer method to be a Fooer, clearly documenting the fact and announcing it in godoc's output.

type Bar struct{}
func (b Bar) ImplementsFooer() {}
func (b Bar) Foo() {}

Most code doesn't make use of such constraints, since they limit the utility of the interface idea. Sometimes, though, they're necessary to resolve ambiguities among similar interfaces.

Himanshu
  • 12,071
  • 7
  • 46
  • 61
  • So, I could do the compile time check and simply return a *Dog instead of Animal in NewDog() right? If I have to explicitly declare, then I will only be able to call the methods in the interface and not in the struct. – Bharat Aug 01 '18 at 15:02
  • 1
    @Bharat that's absolutely right. You can return *Dog struct by which you can access any method which has *Dog struct as receiver. – Himanshu Aug 01 '18 at 15:08
  • Marking this answer as accepted though @shal's answer is also useful since this one has more explanation. – Bharat Aug 01 '18 at 17:27
1

Yes, you can run methods that are not part of the interface. There were two issues in the code preventing it from running properly.

  1. The function NewDog returns the type Animal. The WagTale method is attached to Dog not Animal which gave an error. The return type has been changed to Dog.
  2. Pointers are not needed in this code and have been removed.

After these adjustments, the code runs and properly and all methods execute as expected.

Link on Go playground: https://play.golang.org/p/LYZJiQND7WW

package main

import (
    "fmt"
)


type Animal interface {
    MakeNoise()
}

type Dog struct {
    color string
}

/* Interface implementation */

func (d Dog) MakeNoise() {
    fmt.Println("Bark!")
}

/* End Interface implementation */

func (d Dog) WagTail() {
    fmt.Println(d.color + " dog: Wag wag")
}

func NewDog(color string) Dog {
    return Dog{color}
}

func main() {
    dog := NewDog("Brown")
    dog.MakeNoise()
    dog.WagTail()
    
}
Ugbun
  • 103
  • 1
  • 7