Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are slices passed by value?

In Go, I am trying to make a scramble slice function for my traveling salesman problem. While doing this I noticed when I started editing the slice I gave the scramble function was different every time I passed it in.

After some debugging I found out it was due to me editing the slice inside the function. But since Go is supposed to be a "pass by value" language, how is this possible?

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

I have provided a playground link to show what I mean. By removing line 27 you get a different output than leaving it in, this should not make a difference since the function is supposed to make its own copy of the slice when passed in as an argument.
Can someone explain the phenomenon?

like image 636
duck Avatar asked Oct 12 '16 08:10

duck


People also ask

Is slice passed by value or reference?

When we pass a slice to a function as an argument the values of the slice are passed by reference (since we pass a copy of the pointer), but all the metadata describing the slice itself are just copies.

How do you pass a slice to a function?

As we know that the slice pointer always points to the same reference even if they passed in a function. So, when we change the value C to Java present at index value 2. This change reflects the slice present outside the function too, so the final slice after modification is [C# Python Java perl].

Is Golang pass by value?

Basic of Golang PointerPass by value will pass the value of the variable into the method, or we can say that the original variable 'copy' the value into another memory location and pass the newly created one into the method.

Does Golang pass array by value?

Everything is passed by value. If you assign the value of an array to another variable, the entire value is copied. In memory allocation, and in function, the array is actually a really simple data type, and works in much the same way as structs.


2 Answers

Everything in Go is passed by value, slices too. But a slice value is a header, describing a contiguous section of a backing array, and 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).

So 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.

To see what's in a slice header, check out the reflect.SliceHeader type:

type SliceHeader struct {     Data uintptr     Len  int     Cap  int } 

See related / possible duplicate question: Are Golang function parameter passed as copy-on-write?

Read blog post: Go Slices: usage and internals

like image 95
icza Avatar answered Oct 13 '22 02:10

icza


Slices when its passed it’s passed with the pointer to underlying array, so 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 } 
like image 45
Hasan A Yousef Avatar answered Oct 13 '22 02:10

Hasan A Yousef