Lessons Learned: Accepting “any” type of variables in Golang functions

Scenario

You would like to define a Golang function which accepts different types (or structs) and take different actions depending on your input. This is very similar to the “visitor design pattern”

A good example is marshalling the response of an http.Get call differently depending on your input to this function

Method

The obvious trick is to use interface{} as the input type to the function, then use switch interfaceArg.(type) to determine what action to take

What is not so obvious is that assigning the result of interfaceArg.(type) allows you to modify your input arg in place. This assignment would look like so:

switch v := (interfaceArg).(type)
...

Without this assignment (to v in our example above/below), the compiler would complain that you are attempting to assign mismatched types. With the assignment, the compiler can infer what type the input variable is and so compiles successfully

Example

func GetIfconfigApi(responseStruct interface{}) error {
	
	resp, err := http.Get("http://ifconfig.io/all.json")

	if err != nil {
		fmt.Println("[ERROR] GetIfconfigApi: ", err)
		return err
	}

	body := resp.Body
	defer resp.Body.Close()
	bodyContent, err := io.ReadAll(body)

	if err != nil {
		fmt.Println("[ERROR] GetIfconfigApi: ", err)
		return err
	}

	switch v := (responseStruct).(type) { //this assignment is important
	case *string:
		fmt.Println("hit string")
		*v = string(bodyContent)
	case *[]byte:
		*v = bodyContent
	default:
		json.Unmarshal(bodyContent, responseStruct)
	}
	return nil
}


// Examples of calling the above
type TestStruct struct {
  CountryCode string `json:"country_code"`
  Forwarded   string
}
    
var testStruct TestStruct
var testString string
var testBytes []byte

Tutela.GetIfconfigApi(&testStruct)
fmt.Println(testStruct)
fmt.Println("------")

Tutela.GetIfconfigApi(&testString)
fmt.Println(testString)
fmt.Println("------")

Tutela.GetIfconfigApi(&testBytes)
fmt.Println(testBytes)
fmt.Println("------")