Does Go support call by value or call by reference
Call by value and call by reference are two different ways of passing arguments to a function.
Call By Value
Let’s understand what call by value is. In “call by value”, the argument value is copied into the function’s parameter. This means that any changes made to the parameter inside the function do not affect the original argument value outside the function. The original argument value is protected, and the function only operates on a copy of the value.
func callByValue(a, b int) (int, int) {
temp := a
a = b
b = temp
return a, b
}
Call By Reference
Let’s understand what call by reference is. In “call by reference”, instead of copying the argument value into the function’s parameter, the parameter is a reference (or a pointer) to the original argument. This means that any changes made to the parameter inside the function will affect the original argument value outside the function. The original argument value is not protected and can be modified by the function.
It’s worth noting that “call by reference” can have different variations, such as “pass by reference” and “pass by pointer”. In “pass by reference”, the reference to the argument is passed directly, while in “pass by pointer”, the pointer to the argument is passed. These variations are similar to each other and are often used interchangeably.
func callByRef(c, d *int) (int, int) {
temp := *c
*c = *d
*d = temp
return *c, *d
}
complete code
package main
import (
"fmt"
)
func main() {
fmt.Println("\n=========================")
fmt.Println("Call By Value Code Starts")
fmt.Println("=========================")
x, y := 10, 20
fmt.Printf("Before swap: x = %d, y = %d\n", x, y)
x1, y1 := callByValue(x, y)
fmt.Printf("In function the changed values of x is: %d & y is: %d \n", x1, y1)
fmt.Printf("After swap: x = %d, y = %d\n", x, y)
fmt.Println("\n=========================")
fmt.Println("Call By Value Code Ends")
fmt.Println("=========================")
fmt.Println("\n=============================")
fmt.Println("Call By Reference Code Starts")
fmt.Println("=============================")
u, v := 10, 20
fmt.Printf("Before swap: u = %d, v = %d\n", u, v)
u1, v1 := callByRef(&u, &v)
fmt.Printf("In function the changed values of x is: %d & y is: %d \n", u1, v1)
fmt.Printf("After swap: u = %d, v = %d\n", u, v)
fmt.Println("\n=============================")
fmt.Println("Call By Value Reference Ends")
fmt.Println("=============================")
}
func callByValue(a, b int) (int, int) {
temp := a
a = b
b = temp
return a, b
}
func callByRef(c, d *int) (int, int) {
temp := *c
*c = *d
*d = temp
return *c, *d
}
Result
=========================
Call By Value Code Starts
=========================
Before swap: x = 10, y = 20
In function the changed values of x is: 20 & y is: 10
After swap: x = 10, y = 20
=========================
Call By Value Code Ends
=========================
=============================
Call By Reference Code Starts
=============================
Before swap: u = 10, v = 20
In function the changed values of x is: 20 & y is: 10
After swap: u = 20, v = 10
=============================
Call By Value Reference Ends
=============================
Now let’s understand why we get confused about pass by reference in go
As mentioned above pass by reference & call by reference are most of the times used interchangably.
In general, both terms refer to a way of passing arguments to a function that allows the function to modify the original value of the argument. However, the precise mechanism by which this is achieved can vary between programming languages and implementations.
-
Pass by reference
: In this mechanism, a reference or pointer to the original value of the argument is passed to the function. The function can then use this reference or pointer to modify the original value of the argument. This is typically done in languages that support pointers or references, such as C++ or Java. -
Call by reference
: In this mechanism, the address of the original value of the argument is passed to the function. The function can then use this address to modify the original value of the argument. This is a specific implementation of pass by reference, and is used in some languages, such as Pascal.
Go does not have the concept of “reference variables” in the same way that some other programming languages, such as C++ or Java, do.
In Go, all values are passed by value. When you pass a variable to a function or assign it to another variable, a copy of its value is made. This means that changing the value of the copy does not affect the original variable.
However, Go does have pointers, which are variables that store the memory address of another variable. You can think of a pointer as a “reference” to another variable, but it’s important to note that pointers themselves are variables, not references.
In Go, you can declare a pointer using the *
symbol before the type name, like this:
var x int = 42
var p *int = &x
p
here is a pointer to an integer, and it is initialized with the address of the variable x
. You can then use the *
operator to access the value that p
points to, like this:
fmt.Println(*p) // 42
You can also modify the value that p
points to by dereferencing it with the *
operator and assigning a new value, like this:
*p = 123
fmt.Println(x) // 123
So while Go does not have “reference variables” in the same sense as some other languages, you can use pointers to achieve similar functionality.
See the above Call by Referenc
section for this
Go or Python, don’t have a direct equivalent to pass by reference or call by reference. Instead, they may use other mechanisms, such as pass by value with pointers or pass by object reference, to achieve similar functionality.
Interestingly Go map
& channels
are reference types
A reference type is a type whose value is represented by a memory address. When you assign a reference type to a variable or pass it as an argument to a function, you are actually passing a reference to the memory location where the value is stored, rather than a copy of the value itself.
In the case of maps and channels, this means that if you assign a map or channel to a new variable or pass it as an argument to a function, the new variable or function parameter will point to the same underlying data structure as the original map or channel. This can be useful for sharing data between different parts of your program or for passing large data structures between functions without incurring the overhead of copying the data.
package main
import (
"fmt"
)
func main() {
m := make(map[string]int)
m["a"] = 1
m["b"] = 2
fmt.Println(m) // map[a:1 b:2]
m2 := m
m2["c"] = 3
fmt.Println(m2) // map[a:1 b:2 c:3]
fmt.Println(m) // map[a:1 b:2 c:3]
}
In this example, m
is a map that contains two key-value pairs. We then assign m
to a new variable m2
, and modify m2
by adding a new key-value pair. Because m
and m2
both refer to the same underlying data structure, the modification to m2
also affects m
.
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
ch <- 42
}()
fmt.Println(<-ch) // Output: 42
anotherCh := ch // reference to the same channel
go func() {
anotherCh <- 100
}()
fmt.Println(<-ch) // Output: 100
}
In this example, we create a channel ch
of type int
using the make
function. We then launch a goroutine that sends the value 42
to the channel ch
. We use the channel operator <-
to receive the value from the channel and print it.
We then create another variable anotherCh
and assign it to ch
. This means that anotherCh
now refers to the same channel as ch
.
We launch another goroutine that sends the value 100
to anotherCh
. We then use the channel operator <-
to receive the value from ch
(not anotherCh
) and print it. Since both ch
and anotherCh
refer to the same channel, the value sent to anotherCh
is received from ch
.
It’s worth noting that the direction of the arrow in the channel operator indicates the direction of the data flow. When the arrow points towards the channel variable, it means that data is being sent to the channel. When the arrow points away from the channel variable, it means that data is being received from the channel.
fmt.Println(anotherCh) //0xc000020180
fmt.Println(ch) //0xc000020180
without the arrow(<-) operator it prints the address of the variables & surprise surprise, they are same.