FAQ
I'm getting unexpected behavior when calling an interface within a method,
where the method is implemented by an embedded struct.

Here's an example which illustrates the issue:


package main



import "fmt"



type Automobile struct {



}



type Delorean struct {

         FluxCapacitorMatrix []float64

         Automobile

}



type TurboBoostable interface {

         Boost() string

}



func (auto *Automobile) Run() string {

         return auto.Boost()

}



func (auto *Automobile) Boost() string {

         return "auto"

}



func (delorean *Delorean) Boost() string {

         return "delorean"

}



func main() {

         delorean := &Delorean{}

         boostVal := delorean.Run()

         if boostVal == "auto" {

                 fmt.Println("Test failed: did not call the delorean specific boost() method")

         } else if boostVal == "delorean" {

                 fmt.Println("Test passed: called the delorean specific boost() method")

         }



}



// if this is uncommented, then it will pass

// func (delorean *Delorean) Run() string {

// return delorean.Boost()

// }



  When Delorean does not implement it's own Run() method, and instead lets
the embedded
  struct Automobile provide the Run() method, the Automobile.Run() method
seems to
  lose the type information that the parameter is actually a *Delorean, and
so it
  calls the Automobile's implementation of the TurboBoostable interface
rather than
  the Delorean's implementation.

  As mentioned in commented code, if I give the Delorean it's own version of
the
  Run() method, then things work as expected. However, this is not
desirable, because
  there's nothing that the Delorean needs to do differently in its Run()
method than
  Automobile's Run() method, and so it will just end up as needless code
duplication.

  Is there any changes I can make to make it behave as expected? Or is there
a better
  way to model this so that I wouldn't run into this problem?



--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.

Search Discussions

  • Jesse McNelis at Jun 3, 2013 at 4:17 am

    On Mon, Jun 3, 2013 at 2:01 PM, wrote:

    When Delorean does not implement it's own Run() method, and instead lets
    the embedded
    struct Automobile provide the Run() method, the Automobile.Run() method
    seems to
    lose the type information that the parameter is actually a *Delorean,
    It's not a *Delorean, it's an *Automobile since that's what the method is
    on.
    Embedding is not inheriance. It's only composition with delegation.

    Calling the Run() method on a *Delorean, since the *Delorean doesn't
    implement the method, is
    delegated to the embedded Automobile. The Automobile doesn't know it's
    embedded.

      As mentioned in commented code, if I give the Delorean it's own version of
    the
    Run() method, then things work as expected. However, this is not
    desirable, because
    there's nothing that the Delorean needs to do differently in its Run()
    method than
    Automobile's Run() method, and so it will just end up as needless code
    duplication.

    The different thing it needs to do is to call a method on a different type,
    namely the Delorean and not the Automobile.
    To solve this kind of code sharing Go uses functions that take interface
    values as parameters.
    In your case you can have a function called 'Run' that takes a TurboBoostable
    as a parameter.
    eg.

    func Run(t TurboBoostable) string {

             return t.Boost()

    }

    Now you can pass either an Automobile or a Delorean to it.


    - Jesse
    --
    =====================
    http://jessta.id.au

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
    For more options, visit https://groups.google.com/groups/opt_out.
  • Traun Leyden at Jun 3, 2013 at 5:57 am
    Thanks! That makes sense, and implementing this change fixed my issue.

    Another follow up question, suppose my Run() function needs to check if the
    engine needs servicing before calling boost, and so all Automobiles are
    expected to implement the Serviceable interface.

    Then, my Run() function would change to:

    func Run(t TurboBoostable, s Serviceable) string {

             if !s.needsService() {
                 return t.Boost()
             } else {
                 return "needs_service"
             }


    }

    and calling it would look like:

         Run(delorean, delorean)

    which seems a bit odd. Is that idiomatic Go or is there a better way to achieve that?

    On Sunday, June 2, 2013 9:17:25 PM UTC-7, Jesse McNelis wrote:
    On Mon, Jun 3, 2013 at 2:01 PM, <[email protected] <javascript:>> wrote:

    When Delorean does not implement it's own Run() method, and instead lets
    the embedded
    struct Automobile provide the Run() method, the Automobile.Run() method
    seems to
    lose the type information that the parameter is actually a *Delorean,
    It's not a *Delorean, it's an *Automobile since that's what the method is
    on.
    Embedding is not inheriance. It's only composition with delegation.

    Calling the Run() method on a *Delorean, since the *Delorean doesn't
    implement the method, is
    delegated to the embedded Automobile. The Automobile doesn't know it's
    embedded.

    As mentioned in commented code, if I give the Delorean it's own version
    of the
    Run() method, then things work as expected. However, this is not
    desirable, because
    there's nothing that the Delorean needs to do differently in its Run()
    method than
    Automobile's Run() method, and so it will just end up as needless code
    duplication.

    The different thing it needs to do is to call a method on a different
    type, namely the Delorean and not the Automobile.
    To solve this kind of code sharing Go uses functions that take interface
    values as parameters.
    In your case you can have a function called 'Run' that takes a TurboBoostable
    as a parameter.
    eg.

    func Run(t TurboBoostable) string {


    return t.Boost()


    }


    Now you can pass either an Automobile or a Delorean to it.


    - Jesse
    --
    =====================
    http://jessta.id.au
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
    For more options, visit https://groups.google.com/groups/opt_out.
  • Jesse McNelis at Jun 3, 2013 at 5:34 am

    On Mon, Jun 3, 2013 at 3:13 PM, Traun Leyden wrote:
    Thanks! That makes sense, and implementing this change fixed my issue.

    Another follow up question, suppose my Run() function needs to check if
    the engine needs servicing before calling boost, and so all Automobiles are
    expected to implement the Serviceable interface.

    Then, my Run() function would change to:
    You can just declare the servicing as part of the Turboboostable interface

    type TurboBoostable interface{

    }

    func Run(t TurboBoostable, s Serviceable) string {

    if !s.needsService() {
    return t.Boost()
    } else {
    return "needs_service"
    }


    }

    and calling it would look like:

    Run(delorean, delorean)

    which seems a bit odd. Is that idiomatic Go or is there a better way to achieve that?

    On Sunday, June 2, 2013 9:17:25 PM UTC-7, Jesse McNelis wrote:
    On Mon, Jun 3, 2013 at 2:01 PM, wrote:

    When Delorean does not implement it's own Run() method, and instead
    lets the embedded
    struct Automobile provide the Run() method, the Automobile.Run() method
    seems to
    lose the type information that the parameter is actually a *Delorean,
    It's not a *Delorean, it's an *Automobile since that's what the method is
    on.
    Embedding is not inheriance. It's only composition with delegation.

    Calling the Run() method on a *Delorean, since the *Delorean doesn't
    implement the method, is
    delegated to the embedded Automobile. The Automobile doesn't know it's
    embedded.

    As mentioned in commented code, if I give the Delorean it's own version
    of the
    Run() method, then things work as expected. However, this is not
    desirable, because
    there's nothing that the Delorean needs to do differently in its Run()
    method than
    Automobile's Run() method, and so it will just end up as needless code
    duplication.

    The different thing it needs to do is to call a method on a different
    type, namely the Delorean and not the Automobile.
    To solve this kind of code sharing Go uses functions that take interface
    values as parameters.
    In your case you can have a function called 'Run' that takes a TurboBoostable
    as a parameter.
    eg.

    func Run(t TurboBoostable) string {


    return t.Boost()


    }


    Now you can pass either an Automobile or a Delorean to it.


    - Jesse
    --
    =====================
    http://jessta.id.au

    --
    =====================
    http://jessta.id.au

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
    For more options, visit https://groups.google.com/groups/opt_out.
  • Jesse McNelis at Jun 3, 2013 at 5:37 am

    On Mon, Jun 3, 2013 at 3:13 PM, Traun Leyden wrote:
    Thanks! That makes sense, and implementing this change fixed my issue.

    Another follow up question, suppose my Run() function needs to check if
    the engine needs servicing before calling boost, and so all Automobiles are
    expected to implement the Serviceable interface.

    Then, my Run() function would change to:
    Just do:

    type TurboBoostable interface{
         Boost() string
         needsService() bool
    }

    func Run(t TurboBoostable) string {
    if !t.needsService() {
    return t.Boost()
    } else {
    return "needs_service"
    }
      }

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
    For more options, visit https://groups.google.com/groups/opt_out.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedJun 3, '13 at 4:01a
activeJun 3, '13 at 5:57a
posts5
users2
websitegolang.org

2 users in discussion

Jesse McNelis: 3 posts Traun Leyden: 2 posts

People

Translate

site design / logo © 2023 Grokbase