-1

I will have an application with many messages adhering to a Req/Rsp design. Implemented below is a single Req/Rsp for the message Foo. A Req will adhere to a common interface for processing the message and return a response. All Rsp messages will embed the MHRSP struct. I'd like to set the response status outside of the ProcessRequest method. The issue I am having, is a runtime fault resulting from the block of code below marked Problem Area

panic: interface conversion: interface {} is *main.FooRsp, not main.MHRSP

Since I will have many messages, not just FooRsp, casting to FooRsp is not the solution, so the question is, how do I handle this in a generic way (i.e. able to set Success and Error Message for any Rsp)?

Code compiled using GDB Online: https://www.onlinegdb.com/online_go_compiler

/******************************************************************************
Welcome to GDB Online.
GDB online is an online compiler and debugger tool for C, C++, Python, Java, PHP, Ruby, Perl,
C#, VB, Swift, Pascal, Fortran, Haskell, Objective-C, Assembly, HTML, CSS, JS, SQLite, Prolog.
Code, Compile, Run and Debug online from anywhere in world.
*******************************************************************************/
package main
import (
    "fmt"
    "errors"
    "encoding/json"
)

type MHREQIF interface {
    ProcessRequest(e int) (interface{}, error)
}

type MHRSP struct {
    Success bool `json:"success,omitempty"`
    ErrorMessage string `json:"error_message,omitempty"`
}

type FooReq struct {
    MHREQIF
    Name string `json:"name"`
}

type FooRsp struct {
    MHRSP
    Name string `json:"name"`
}

func (self *FooReq) ProcessRequest(e int) (interface{}, error) {
    rsp := FooRsp{Name:self.Name}
    if 0 == e {
        return &rsp, nil    
    } else {
        return &rsp, errors.New("crap")
    }
}

func main() {
    var msg_req MHREQIF
    msg_req = &FooReq{Name:"bob"}
    rsp, err := msg_req.ProcessRequest(1)
    if err != nil {
        // -------------- PROBLEM AREA BEG ------------------
        v := rsp.(MHRSP)
        v.Success = false
        v.ErrorMessage = fmt.Sprintf("%v", err)
        // -------------- PROBLEM AREA END ------------------
    }
    msg_bytes, _ := json.Marshal(rsp)
    msg_string := string(msg_bytes)
    fmt.Println(msg_string)
}
pyInTheSky
  • 1,459
  • 1
  • 9
  • 24
  • 1
    Declare an interface, have MHRSP implement that interface, by embedding the common type all "embedders" will also implement that interface automatically, then use the interface to set the fields. – mkopriva May 20 '20 at 17:04
  • 1
    i.e. instead of the empty interface `interface{}` have `ProcessRequest` return an interface that has methods that can be used to set the fields' values on the common type. – mkopriva May 20 '20 at 17:07
  • 1
    Yeah the problem here is the inappropriate use of the empty interface. It's practically never correct for a function to *return* a `interface{}`; it's *sometimes* correct for a function to *take* a `interface{}` as an argument. – Adrian May 20 '20 at 17:10
  • Thank you @mkopriva and Adrian – pyInTheSky May 20 '20 at 19:00

1 Answers1

1

Thanks to @mkopriva and @Adrian for pointing me in the right direction. Sometimes that shift from C++ OO to go-isms is mind bending :P

Here is the working code for anyone with a similar struggle on how to tackle this kind of issue.

/******************************************************************************
Welcome to GDB Online.
GDB online is an online compiler and debugger tool for C, C++, Python, Java, PHP, Ruby, Perl,
C#, VB, Swift, Pascal, Fortran, Haskell, Objective-C, Assembly, HTML, CSS, JS, SQLite, Prolog.
Code, Compile, Run and Debug online from anywhere in world.
https://www.onlinegdb.com/online_c++_compiler
*******************************************************************************/
package main
import (
    "fmt"
    "errors"
    "encoding/json"
)

type MHREQIF interface {
    ProcessRequest(e int) (MHRESPIF, error)
}

type MHRESPIF interface {
    SetResponse(err error)
}

type MHRSP struct {
    MHRESPIF `json:"-"`
    Success bool `json:"success"`
    ErrorMessage string `json:"error_message,omitempty"`
}

type FooReq struct {
    MHREQIF
    Name string `json:"name"`
}

type FooRsp struct {
    MHRSP
    Name string `json:"name"`
}

func (self *MHRSP) SetResponse(err error) {
    if err != nil {
        self.Success = false
        self.ErrorMessage = fmt.Sprintf("%v", err)
    } else {
        self.Success = true
    }
}

func (self *FooReq) ProcessRequest(e int) (MHRESPIF, error) {
    rsp := FooRsp{Name:self.Name}
    if 0 == e {
        return &rsp, nil    
    } else {
        return &rsp, errors.New("crap")
    }
}

func main() {
    var msg_req MHREQIF
    msg_req = &FooReq{Name:"bob"}
    rsp, err := msg_req.ProcessRequest(1)
    rsp.SetResponse(err)
    msg_bytes, _ := json.Marshal(rsp)
    msg_string := string(msg_bytes)
    fmt.Println(msg_string)
}
pyInTheSky
  • 1,459
  • 1
  • 9
  • 24