Search code examples
gogo-interface

Golang Access Data Elements of Common Struct


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)
}

Solution

  • 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)
    }