Returning a pointer to a struct from a function in Go

Issue

I have two public structs that contain different data, and a private intermediate struct containing either of the two public structs. I also have a function that unmarshalls the intermediate struct, determines which public struct it contains, and returns one of the two public structs.

The problem I’m facing is the return value of the last function. At it’s simplest I thought I could return *struct{} but I keep getting a type mismatch in my IDE.

I apologize for posting more code than is probably necessary, but I’m trying to make it as close as possible to the code I’m working on.

package main

import (
    "encoding/json"
    "errors"
)

// These vars are some errors I'll use in the functions later on
var (
    errInvalidBase64     = errors.New("invalid base64")
    errInvalidStructType = errors.New("invalid struct type")
)

// Struct1 public struct
type Struct1 struct {
    FName string `json:"first-name"`
    LName string `json:"last-name"`
}

// Struct2 public struct
type Struct2 struct {
    Date  string `json:"date"`
    Items []int  `json:"items"`
}

// intermediateStruct private struct
// The Type field indicates which kind of struct Data contains (Struct1 or Struct2)
// The Data field contains either Struct1 or Struct2 which was previously marshalled into JSON
type intermediateStruct struct {
    Type structType
    Data []byte
}

// The following type and const are my understanding of an enum in Go

// structType is a private type for the type of struct intermediateStruct contains
type structType int

// These public constants are just to keep my hands out of providing values for the different struct types
const (
    StructType1 structType = iota
    StructType2
)

// unmarshalStruct1 unmarshalls JSON []byte into a new Struct1 and returns a pointer to that struct
func unmarshalStruct1(b []bytes) (*Struct1, error) {
    newStruct1 := new(Struct1)
    err := json.Unmarshal(b, newStruct1)
    if err != nil {
        return nil, errInvalidBase64
    }
    return newStruct1, nil
}

// unmarshalStruct2 unmarshalls JSON []byte into a new Struct2 and returns a pointer to that struct
func unmarshalStruct2(b []bytes) (*Struct2, error) {
    newStruct2 := new(Struct2)
    err := json.Unmarshal(b, newStruct2)
    if err != nil {
        return nil, errInvalidBase64
    }
    return newStruct2, nil
}

// receiveStruct accepts *intermediateStruct who's Data field contains either Struct1 or Struct2
// This function needs to return either *Struct1 or *Struct2 and an error
func receiveStruct(iStruct *intermediateStruct) (*struct{}, error) {
    switch iStruct.Type {
    case StructType1:
        struct1, err := unmarshalStruct1(iStruct.Data)
        if err != nil {
            return nil, err
        }
        // The following line is where I'm getting the type mismatch
        return struct1, nil
    case StructType2:
        struct2, err := unmarshalStruct2(iStruct.Data)
        if err != nil {
            return nil, err
        }
        // The following line is another type mismatch
        return struct2, nil
    default:
        return nil, errInvalidStructType
    }
}

I know there’s a way to do what I’m trying to achieve – I just lack the experience/understanding to get there.

Thanks for any and all input!

Solution

Your unmarshallStruct function returns a pointer to type Struct1 or Struct2 (depending on which version of the function gets called). And therefore the variables struct1 and struct2 are pointers to types Struct1 and Struct2 respectively. Neither is a pointer to type struct (which is not a real Go type anyways I must add). A struct is a keyword which helps to declare types containing fields/attributes.

Depending on the use-cases you have in mind for the rest of your code, can instead try any of the below:

  1. As mkopriva suggested, return a interface{} object, but you’d need to use type assertion to actually make sure of the object
  2. Define an interface which both Struct1 and Struct2 implement, and return a pointer to this
  3. Make separate functions which work with either Struct1 or Struct2. This is not necessarily as bad as it sounds as Go lets you pass functions in the same way you’d pass types (see example of the Less() function in sort package).

Answered By – Sangam

Answer Checked By – Candace Johnson (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.