-2

Given this code...

type BaseItf1 interface {
  getName() string
  clone() *BaseStruct
}

type BaseStruct struct {
  BaseItf1
}

func (bs *BaseStruct) cloneAndGetName() string {
  sc := bs.clone()
  return sc.getName()
}

type SubClass struct {
  BaseStruct
}

func (sc *SubClass) getName() string {
  return "A"
}

func (sc *SubClass) clone() *SubClass {
  return &SubClass{}
}

func main() {
  sc := &SubClass{}
  fmt.Printf("-> %s\n", sc.clone().getName())
  fmt.Printf("-> %s\n", sc.cloneAndGetName())
}

I can't quite figure out why I'm getting this error:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0x2004a]

The call to clone in main works perfectly, naturally.

In cloneAndGetName, however, the clone method can't be invoked. bs is typed to a pointer to BaseStruct, which has the BaseItf interface with the clone method. It would seem like the concrete sc instance in main that invoked cloneAndGetName knows how to locate the clone method.

What am I missing? Is there a better way to go about this? In my real code, I need a way to create a new instance of an object from some shared code.

object88
  • 720
  • 1
  • 7
  • 20
  • The first call to `sc.clone()` gives the error you're seeing, and not the call in `cloneAndGetName`. You can replace main with `sc := &SubClass{}; sc.clone()` to verify. – Paul Hankin Nov 21 '16 at 03:23
  • Not sure about that, Paul. If I comment `fmt.Printf("-> %s\n", sc.clone().getName())`, the error continues. – object88 Nov 21 '16 at 04:00
  • Both are erroneous, but the one that appears first (the `sc.clone()` in main) causes the error you're seeing. If you comment it out, you get the same error from the other command. The text of your question suggested that you believed that only the second (the code in `cloneAndGetName`) causes the error, and that "the call to `clone` in `main` works perfectly, naturally." – Paul Hankin Nov 21 '16 at 04:26
  • Commenting out the call to `sc.cloneAndGetName` and leaving in `sc.clone().getName()` in main, does work. (I did find a naming error in my originally posted code; though; fixed now.) – object88 Nov 21 '16 at 04:34
  • yes, now it works after you changed the code :P – Paul Hankin Nov 21 '16 at 14:32
  • Again, my apologies for that. Giving incorrect example code does nothing when I'm asking for help. Thanks! – object88 Nov 21 '16 at 15:16

4 Answers4

2

bs.clone() fails because it's trying to call bs.BaseItf1.clone() and bs.BaseItf1 is nil. You can't call the clone() defined for *SubClass from a variable of type *BaseStruct. Embedding types in Go is not the same as subclassing in other languages.

Andy Schweig
  • 6,597
  • 2
  • 16
  • 22
  • Indeed. I've read numerous examples of exactly this problem, but I still failed to see it in my own code. Thanks! – object88 Nov 21 '16 at 15:17
1

You're confusing embedding with interface fulfillment. In its current state, you have a struct SubClass which embeds the struct BaseStruct, which then embeds an interface BaseItf1. However, you have a problem: SubClass does not override the cloneAndGetName() method. As such, this method is called on the embedded BaseStruct struct. Embedded method calls are invoked using the embedded struct as the receiver, not the embedding struct. The BaseStruct struct thus only has access to its own methods, not those of the SubClass struct wrapping it. Since those methods themselves are the result of the embedded BaseItf1 interface, BaseStruct calls those methods on that embedded interface, which is nil. This, of course, triggers a segfault.

In this case, it appears that you want to have a standard method defined on some base structure, but then be able to override that behavior with a subclass. In this case, place an instance of SubClass into the interface field within the BaseClass:

sc := BaseStruct{&SubClass{}}
fmt.Printf("-> %s\n", sc.clone().getName())
fmt.Printf("-> %s\n", sc.cloneAndGetName())

In this instance, sc.closeAndGetName() will be called on the BaseStruct, but the clone() and getName() calls will occur on the substruct.

You can even define default behavior if the interface is nil:

type BaseItf1 interface {
  getName() string
  clone() *BaseStruct
}

type BaseStruct struct {
  sub BaseItf1
}

func (bs *BaseStruct) cloneAndGetName() string {
  sc := bs.clone()
  return sc.getName()
}

func (bs *BaseStruct) getName() string {
  if bs.sub != nil {
    return bs.sub.getName()
  }
  // default behavior
  return "<nil>"
}

func (bs *BaseStruct) clone() *BaseStruct {
  if bs.sub != nil {
    return bs.sub.clone()
  }
  // default behavior
  return bs
}

type SubClass struct {
  // Doesn't need to embed BaseStruct
  // ...
}

func (sc *SubClass) getName() string {
  return "A"
}

func (sc *SubClass) clone() *BaseStruct {
  return &BaseStruct{&SubClass{}}
}

https://play.golang.org/p/CWdScZMXZ_

Kaedys
  • 9,600
  • 1
  • 33
  • 40
0

you should implement the function in the interface first.

package main

import (
    "fmt"
)

type BaseItf1 interface {
    getName() string
    clone() *BaseStruct
}

type BaseStruct struct {
    BaseItf1
}

func (bs *BaseStruct) cloneAndGetName() string {
    sc := bs.clone()
    return sc.getName()
}

func (bs *BaseStruct) getName() string {
    return ""
}

func (bs *BaseStruct) clone() *BaseStruct {
    return nil
}

type SubClass struct {
    BaseStruct
}

func (sc *SubClass) getName() string {
    return "A"
}

func (sc *SubClass) clone() *SubClass {
    return &SubClass{}
}

func main() {
    sc := &SubClass{}
    fmt.Printf("-> %s\n", sc.clone().getName())
    fmt.Printf("-> %s\n", sc.cloneAndGetName())
}
duguying
  • 113
  • 6
0

When you have a struct and you want to satisfy an interface you have to implement all the methods of that interface, it's different when you have an interface with other interface inside, for structs it's just having a field, if you check in the code sc.BaseItf1 has a type and value , so the call to .clone() and .cloneAndGetName() are failing. Implemented the methods here:

type BaseItf1 interface {
    getName() string
    clone() *BaseStruct
}

type BaseStruct struct {
    BaseItf1 // shortcut to BaseItf1 BaseItf1  <fieldName> <Type>
}

func (bs *BaseStruct) clone() *BaseStruct {
    return &BaseStruct{}
}

func (bs *BaseStruct) getName() string {
    //
    // maybe BaseStruct should have a field Name, not sure if you want to
    // in that case we can do:
    //
    // return bs.Name

    return "a BaseStruct has no name"
}

func (bs *BaseStruct) cloneAndGetName() string {
    sc := bs.clone()
    return sc.getName()
}

type SubClass struct {
    BaseStruct
}

func (sc *SubClass) methodA() string {
  return "A"
}

func (sc *SubClass) methodB() *SubClass {
  return &SubClass{}
}

func main() {
    sc := &SubClass{}

    fmt.Printf("type: %T and value: %v \n\n", sc.BaseItf1, sc.BaseItf1)

    fmt.Printf("-> %q\n", sc.clone().getName())
    fmt.Printf("-> %q\n", sc.cloneAndGetName())
}

https://play.golang.org/p/tHScVktrDZ

Yandry Pozo
  • 4,851
  • 3
  • 25
  • 27
  • I had some incorrect method declarations initially; have corrected them above. However, following your example doesn't give the anticipated / desired error. The method overrides in `SubClass` are not invoked, just the methods in `BaseStruct`. – object88 Nov 21 '16 at 04:27