Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
Pass Interface Parameters by Reference in Golang
A simple diary about a simple thing that I learned about Pass By Reference in Golangpass by reference on interface parameter in Golang
Back to the college days, I remember there are 2 ways to pass a parameter to a function. One passes by value, and the other one passes by reference. Both of these ways have a different concept and sometimes it brings confusion to the programmer.
In simple terms, pass by value is when we pass the parameter without a pointer to that origin address of the value. And pass by reference is when we pass the parameter with a pointer to the given parameter.
In Golang, we can see these two in this example below.
func passByValue(item string){}func passByReference(item *string){}
Pass By Reference and Pass By Value in Golang
Actually, there are already many examples of how to do pass by reference and pass by value in Golang that we can find on the Internet.
So here, I will make it a simple example.
package main
import ( "fmt")
func main() { item := "" passByValue(item) fmt.Println(item) passByReference(&item) fmt.Println(item)}
func passByValue(item string) { item = "hello"}
func passByReference(item *string) { *item = "world"}
The above example shows a typical way we might pass the parameters based on their types (by references or by value).
Pass By Reference on Interface Param in Golang
But, one day. I faced a problem that I needed to solve about passing a parameter by reference. Not like any ordinary one that I’ve ever made, this one using an interface at the parameter. So basically, this function accepts anything in interface{}, and fills the value based on the logic that happens inside that function.
The function looks like this below.
func doSomethinWithThisParam(item interface{}){}
This function is simple, it only accepts an interface, and does something inside it. It won’t return any error, so it just to hydrate the item with a value in it.
So to solve this weird behavior, I tried to solve with trying it on my own. Here below I will explain the steps how I solve it.
First Attempt: Pointer to Interface [Not Worked]
At first, I tried to like the non-interface{} does. I put a pointer in the interface. But it doesn’t work.
package main
import ( "fmt")
func main() { var item Student doSomethinWithThisParam(&item) fmt.Printf("%+v", item)}
type Student struct { ID string Name string}
func doSomethinWithThisParam(item *interface{}) { *item = &Student{ ID: "124", Name: "Iman Tumorang", }}
This one can’t be compiled, it throws the error.
cannot use &item (type *Student) as type *interface {} in argument to doSomethinWithThisParam: *interface {} is pointer to interface, not interface
Second Attempt: Directly Assign Value to Interface [Not Worked]
The second one, I try without a pointer to the interface, but instead, I assign the value directly to the given param.
func doSomethinWithThisParam(item interface{}) { item = &Student{ ID: "124", Name: "Iman Tumorang", }}
// Print: {ID: Name:}
And after print the data, it still not worked. It prints the empty value.
Third Attempt: Casting to Original Type and Assign the Value [Worked but….]
Later, after trying many things, I found a worked one. The parameter is still an interface{}, but instead of directly assign the value, at first I had to cast it back to the original’s type. At this time, it’s a bit tricky. And we must careful how to use it. See the difference here below.
Not worked oneThis one example below is not worked.
func doSomethinWithThisParam(item interface{}) { origin := item.(*Student) origin = &Student{ ID: "124", Name: "Iman Tumorang", } item = origin}
// Print: {ID: Name:}
Worked oneBut this one is worked.
func doSomethinWithThisParam(item interface{}) { origin := item.(*Student) origin.Name = "Iman Tumorang" origin.ID = "124"}
// Print: {ID:124 Name:Iman Tumorang}
Seriously????? 😱At first, I’m a bit confused. What is really happening here? How could be when I made like this one it doesn’t work.
origin := item.(*Student) origin = &Student{ ID: "124", Name: "Iman Tumorang", }
But with this one, it worked.
origin := item.(*Student)origin.Name = "Iman Tumorang"
Need a few minutes to figure this out. But later I understand why this happens.
Another worked oneAfter figuring the problem, I realized something. The first one is failed since it replaces the address. So instead to replace the address, I try a new approachment that only change the value.
func doSomethinWithThisParam(item interface{}) { origin := item.(*Student)*origin = Student{ ID: "124", Name: "Iman Tumorang", } item = origin}
// Print: {ID:124 Name:Iman Tumorang}
This one is worked well. That made me realized that when we want to change the value in the pointer variable, we need to set directly to the value, not to change address itself.
Final ResolverSo after experimenting with many trials, finally I choose the last one. And because this function that I’m working on will a bit generic based on my current task, I transform it and add switch case condition so it will be more generic based on the switch case I made.
In simple, All my works on my current task can be described in this example below. There is a generic function that will accept interface{} and do something inside it. And it supports many structs.
The snippet code would be like this:
Conclusions
This one is really-really a serious thing in Golang. We must very careful when working with pass-by-reference and interface{}. To avoid any unnecessary bugs, I recommend adding a unit test to each function that uses the pass-by-reference method.
To be honest, I’m stuck for an hour on this issue. So if you think this is a good thing to knows, kindly share this article so anyone won’t fall to the same problems.
Today I Learned: Pass By Reference on Interface Parameter in Golang was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.