Are arrays passed by value or passed by reference in GO?

I’ve the below code:

package main

import "fmt"

func main() {
	arr := []string{"hi", "bye"}
	fmt.Printf("arr: %v\n", arr)
	check(arr)
	fmt.Printf("arr: %v\n", arr)
}

func check(c []string) {
	for index, _ := range c {
		c[index] = "ok"
	}
}

That gave the following output:

arr: [hi bye]
arr: [ok ok]

Which means array got passed as reference not as value, is my understanding correct? Is there any official documentation about this?

Is there anything else other than arrays passed as reference in GO?

1 Like

Hi @hyousef,

Slice is passed by value. When you will append new value in the check function, it will not reflect in the main function.
See here: https://play.golang.org/p/cTB7C6ws7Uo

When you pass the address of the slice in check function, it will reflect in the main function.
See here: https://play.golang.org/p/x50H9C7Rw2X

Regards
Prithvi

3 Likes

Explicit function parameters are always passed by value. Arrays, slices, structs, etc. are all passed by value. The way you “pass by reference” is to pass a pointer (which itself is still passed by value). You can then dereference the pointer.

Slices, though passed by value, wrap a pointer to the underlying array, so even though the function passes by value, it behaves as if it’s “by reference” because the slice is mutated through that pointer.

https://play.golang.org/p/BYvLdnGWxup

2 Likes

arr as you have defined it is not an array it is a slice. An array would look like this

arr := [2]string{“hi”, “bye”}

This would mean that you could not change the size of the array arr once you have instantiated it. You could change the values but not increase or decrease the number of elements in the array. A slice allows you to increase and decrease the number of elements, copy the slice etc. A slice is a reference to an underlying array. Arrays are not used so much in Go but Slices are.

2 Likes

Hi.
In both codes the values of the original slice that passed to the function had been changed!

Golang default is pass by Value.
There is no pass by reference like in c++.

Slices when its passed it’s passed with the pointer to underlying array.

1 Like

Thanks for those who replied, with your support and some extra search based on it I found the answer:

Slices when its passed it’s passed with the pointer to underlying array, a slice value only contains a pointer to the array where the elements are actually stored. The slice value does not include its elements (unlike arrays). slice value is a header , describing a contiguous section of a backing array, when you pass a slice to a function, a copy will be made from this header, including the pointer, which will point to the same backing array. Modifying the elements of the slice implies modifying the elements of the backing array, and so all slices which share the same backing array will “observe” the change.
A slice is a small structure that points to an underlying array. The small structure is copied, but it still points to the same underlying array. the memory block containing the slice elements is passed by “reference”. The slice information triplet holding the capacity, the number of element and the pointer to the elements is passed by value.

The best way to handle slices passing to function (if the elements of the slice are manipulated into the function, and we do not want this to be reflected at the elements memory block is to copy them using copy(s, *c) as:

package main

import "fmt"

type Team []Person
type Person struct {
    Name string
    Age  int
}

func main() {
    team := Team{
        Person{"Hasan", 34}, Person{"Karam", 32},
    }
    fmt.Printf("original before clonning: %v\n", team)
    team_cloned := team.Clone()
    fmt.Printf("original after clonning: %v\n", team)
    fmt.Printf("clones slice: %v\n", team_cloned)
}

func (c *Team) Clone() Team {
    var s = make(Team, len(*c))
    copy(s, *c)
    for index, _ := range s {
        s[index].Name = "change name"
    }
    return s
}

But be careful, if this slice is containing a sub slice further copying is required, as we’ll still have the sub slice elements sharing pointing to the same memory block elements, an example is:

type Inventories []Inventory
type Inventory struct { //instead of: map[string]map[string]Pairs
    Warehouse string
    Item      string
    Batches   Lots
}
type Lots []Lot
type Lot struct {
    Date  time.Time
    Key   string
    Value float64
}

func main() {
ins := Inventory{
        Warehouse: "DMM",
        Item:      "Gloves",
        Batches: Lots{
            Lot{mustTime(time.Parse(custom, "1/7/2020")), "Jan", 50},
            Lot{mustTime(time.Parse(custom, "2/1/2020")), "Feb", 70},
        },
    }

   inv2 := CloneFrom(c Inventories)
}

func (i *Inventories) CloneFrom(c Inventories) {
    inv := new(Inventories)
    for _, v := range c {
        batches := Lots{}
        for _, b := range v.Batches {
            batches = append(batches, Lot{
                Date:  b.Date,
                Key:   b.Key,
                Value: b.Value,
            })
        }

        *inv = append(*inv, Inventory{
            Warehouse: v.Warehouse,
            Item:      v.Item,
            Batches:   batches,
        })
    }
    (*i).ReplaceBy(inv)
}

func (i *Inventories) ReplaceBy(x *Inventories) {
    *i = *x
}
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.