Modify slice element using pointer - go

I'm currently trying to modify an element of a slice by passing a pointer to a function. Outside of the function the element is not modified.
Is there a way to modify the element without passing the slice itself along with the index of the desired element to alter?
package main
import (
"fmt"
)
type Item struct {
Value int
}
func alter(t *Item) {
(*t).Value = 100
}
func main() {
items := []Item{Item{0}, Item{1}}
for _, item := range items {
alter(&item)
}
fmt.Println(items) // Output is still [{0} {1}]
}
Golang Playground

for i := range items {
alter(&items[i])
}
Or
items := []*Item{{0}, {1}}
for _, item := range items {
alter(item)
}
The reason your version doesn't work is because the iteration variable item holds a copy of the element inside the slice, which means that what you're modifying is the copy and not the original. You can see that they are separate objects in memory if you run this: https://play.golang.org/p/vr9CfX0WQcB

Refer: https://tour.golang.org/moretypes/16
The range form of the for loop iterates over a slice or map.
When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.
So,
for i, x := range arr {
// x is copy for arr[i]
}
Hence, we will directly used arr[i] and pass the address of the same to the alter function so that it could be modified.
Sample code:
package main
import "fmt"
type Item struct {
Value int
}
func alter(t *Item) {
(*t).Value = 100
}
func main() {
items := []Item{{0}, {1}}
for i := range items {
alter(&items[i])
}
fmt.Println(items)
}
[Playground]

Related

Add data to slice when use range with go

I wanna append some data when range the slice, like this:
package main
import "fmt"
func main() {
slices := []string{"item-1", "item-2", "item-3"}
for _, item := range slices {
if item == "item-2" {
slices = append(slices, "item-5")
}
fmt.Println(item)
}
}
the code output:
item-1
item-2
item-3
I expect:
item-1
item-2
item-3
item-5
Similar to this syntax in python:
slices = ["item-1", "item-2", "item-3"]
for item in slices[::]:
if item == "item-2":
slices.append("item-5")
print(item)
How it should be implemented in Go?Thanks
i try to search in this website and google, use the Add data to slice when use range with go keyword.
Instead of using range, iterate explicitly with a counter
func main() {
slices := []string{"item-1", "item-2", "item-3"}
for i := 0; i < len(slices); i++ {
item := slices[i]
if item == "item-2" {
slices = append(slices, "item-5")
}
fmt.Println(item)
}
}
Because you re-assign slices in the loop, you need to re-check the len every iteration to see how long it is currently. The built-in range only iterates over the initial value of slices; it doesn't see any updates to the slice definition that happen during iteration.

Why [capacity]string assert to []string will be failed in Golang?

I am using Golang1.14.
Here is the test code.
package main
import "time"
func checkData(data interface{}) {
if _, ok := data.([]string); ok {
println("Assert true.")
} else {
println("Assert false.")
}
}
func main() {
var years [20]string
for i := 0; i < 20; i++ {
years[i] = string(time.Now().Year() - 10 + i)
}
checkData(years)
foods := []string{"Fruit", "Grass", "Fish", "Meat"}
checkData(foods)
}
The output is:
Assert false.
Assert true.
I am new to Golang and really confusing that [20]string is not a []string.Can someone tell me why?Thanks.
[20]string is an array. It is a type that contains 20 strings, and if you pass it as an interface{}, you can recover it using intf.([20]string).
[]string is a slice. It has a backing array, but it is essentially a view over an array. You assertion checks if the interface is a slice, so this one works.
Arrays and slices are different things in Go. An array is a data type with a fixed size. For instance:
func f(arr [10]int) {...}
You can only call f with an int array of size 10. When you do call it, the array will be passes as value, so the function will get a copy of the array, all 10 members of it. But:
func f(arr []int) {...}
You can call f with any size of slice. A slice contains a reference to its underlying array, so an array copy will not take place here. You cannot call thisf` with an array.

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?
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:
Performance of function slice parameter vs global variable?
Read blog post: Go Slices: usage and internals
Please note that when you pass a slice to a function, if the function modifies the "existing" elements of the slice, the caller will see / observe the changes. If the function adds new elements to the slice, that requires changing the slice header (the length at a minimum, but may also involve allocating a new backing array), which the caller will not see (not without returning the new slice header).
Not with maps, because maps are pointers under the hood, and if you pass a map to a function and the function adds a new entry to the map, the map pointer will not change so the caller will see the changed map (the new entry) without returning the map after change.
Also regarding slices and maps, see Map initialization in Go and why slice values can sometimes go stale but never map values?
You can find an example below. Briefly slices is also passed by value but original slice and copied slice are linked to the same underlying array. If one of this slice changes, then underlying array changes, then other slice changes.
package main
import "fmt"
func main() {
x := []int{1, 10, 100, 1000}
double(x)
fmt.Println(x) // ----> 3 will print [2, 20, 200, 2000] (original slice changed)
}
func double(y []int) {
fmt.Println(y) // ----> 1 will print [1, 10, 100, 1000]
for i := 0; i < len(y); i++ {
y[i] *= 2
}
fmt.Println(y) // ----> 2 will print [2, 20, 200, 2000] (copy slice + under array changed)
}
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
}
Slice will work with pass by value to the function, But we should not use append to add values to slice in the function, instead we should use the assignment directly. Reason being that append will create new memory and copy values to that. Here is the example.
Go playground
// Go program to illustrate how to
// pass a slice to the function
package main
import "fmt"
// Function in which slice
// is passed by value
func myfun(element []string) {
// Here we only modify the slice
// Using append function
// Here, this function only modifies
// the copy of the slice present in
// the function not the original slice
element = append(element, "blackhole")
fmt.Println("Modified slice: ", element)
}
func main() {
// Creating a slice
slc := []string{"rocket", "galaxy", "stars", "milkyway"}
fmt.Println("Initial slice: ", slc)
//slice pass by value
myfun(slc)
fmt.Println("Final slice: ", slc)
}
Output-
Initial slice: [rocket galaxy stars milkyway]
Modified slice: [rocket galaxy stars milkyway blackhole]
Final slice: [rocket galaxy stars milkyway]
Go Playground
// Go program to illustrate how to
// pass a slice to the function
package main
import "fmt"
// Function in which slice
// is passed by value
func myfun(element []string) {
// Here we only modify the slice
// Using append function
// Here, this function only modifies
// the copy of the slice present in
// the function not the original slice
element[0] = "Spaceship"
element[4] = "blackhole"
element[5] = "cosmos"
fmt.Println("Modified slice: ", element)
}
func main() {
// Creating a slice
slc := []string{"rocket", "galaxy", "stars", "milkyway", "", ""}
fmt.Println("Initial slice: ", slc)
//slice pass by value
myfun(slc)
fmt.Println("Final slice: ", slc)
}
Output-
Initial slice: [rocket galaxy stars milkyway ]
Modified slice: [Spaceship galaxy stars milkyway blackhole cosmos]
Final slice: [Spaceship galaxy stars milkyway blackhole cosmos]
To complement this post, here is an example of passing by reference for the Golang PlayGround you shared:
type point struct {
x int
y int
}
func main() {
data := []point{{1, 2}, {3, 4}, {5, 6}, {7, 8}}
makeRandomDatas(&data)
}
func makeRandomDatas(dataPoints *[]point) {
for i := 0; i < 10; i++ {
if len(*dataPoints) > 0 {
fmt.Println(makeRandomData(dataPoints))
} else {
fmt.Println("no more elements")
}
}
}
func makeRandomData(cities *[]point) []point {
solution := []point{(*cities)[0]} //create a new slice with the first item from the old slice
*cities = append((*cities)[:0], (*cities)[1:]...) //remove the first item from the old slice
return solution
}

How to return slice by reference?

The returned slice by reference is empty:
package main
import "fmt"
func GetItems(items *[]string) {
list := make([]string, 0)
list = append(list, "ok")
items = &list
}
func main() {
var items []string
GetItems(&items)
fmt.Print(len(items)) // expect 1 here, but got 0
}
How to return the slice from the function by reference?
By assigning to items, you alter where items points, not the value items points to. To do the latter, instead of items = &list write *items = list.

Go weird behaviour - variable not incrementing correctly

I have the following code that adds a new element to a slice if it doesnt exist already. If it does exist then the qty property should be incremented of the existing element instead of a new element being added:
package main
import (
"fmt"
)
type BoxItem struct {
Id int
Qty int
}
type Box struct {
BoxItems []BoxItem
}
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// If the item exists already then increment its qty
for _, item := range box.BoxItems {
if item.Id == boxItem.Id {
item.Qty++
return item
}
}
// New item so append
box.BoxItems = append(box.BoxItems, boxItem)
return boxItem
}
func main() {
boxItems := []BoxItem{}
box := Box{boxItems}
boxItem := BoxItem{Id: 1, Qty: 1}
// Add this item 3 times its qty should be increased to 3 afterwards
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
fmt.Println(len(box.BoxItems)) // Prints 1 which is correct
for _, item := range box.BoxItems {
fmt.Println(item.Qty) // Prints 1 when it should print 3
}
}
The problem is that the qty is never incremented correctly. It always ends in 1 when it should be 3 in the example provided.
I have debugged the code and it does appear that the increment section is reached but the value just isnt persisted to the item.
What is wrong here?
You are incrementing Qty in the copy of the box.BoxItems because range will yield the copy of the elements in the slice. See this example.
So, in for _, item := range box.BoxItems, item is a copy of of the elements in box.BoxItems.
Change your loop to
for i := 0; i < len(box.BoxItems); i++ {
if box.boxItems[i].Id == boxItem.Id {
box.boxItems[i].Qty++
return box.BoxItems[i]
}
}
Playground
I will answer your question pretty much like others have done. However, not that the problem you try to solve is not best served by looping over a range of values. Read on:
Solution to your question
Like others have said, for-range provide an immutable iteration over the range of values. That means any change you make to the value provided in the iteration will be lost. It's basically giving you a copy of the real value, not the actual value.
for _, item := range box.BoxItems {
// ^-not the real `item`, it's a copy!
A way around this is to keep track of the indexing value in the for idx, val := range, and use this idx to address the value you look for directly.
If you change your for-loop to keep the index value:
for i, item := range box.BoxItems {
// ^-keep this
You will be able to reference the actual item in the array you loop on:
for i, item := range box.BoxItems {
// Here, item is a copy of the value at box.BoxItems[i]
if item.Id == boxItem.Id {
// Refer directly to an item inside the slice
box.BoxItems[i].Qty++
return box.BoxItems[i] // Need to return the actual one, not the copy
}
}
Playground
I would favor this approach over the for i; i<Len; i++ one as I find it more readable. But this is simply a matter of taste and the for i form will be more efficient (beware of premature-optimization!).
Your real problem is
What you're trying to do is to avoid duplicating BoxItems if their Id already exists. To do this, you iterate over the whole range of the box.BoxItems slice. If you have N items in your box.BoxItems slice, you will potentially iterate over all N items before finding out that the item you're looking for doesn't exist! Basically, this means your algorithm is O(N).
If you increment Id in natural order
That is, 0, 1, 2, 3, ..., n - 1, n, you can keep using a slice to index your box items. You would do like this:
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// Lookup your item by Id
if boxItem.Id < len(box.BoxItems) {
// It exists, do don't create it, just increment
item := box.BoxItems[boxItem.Id]
item.Qty++
box.BoxItems[boxItem.Id] = item
return item
}
// New item so append
box.BoxItems = append(box.BoxItems, boxItem)
return boxItem
}
Playground
If you increment Id in any order
You should use a datastructure that offers fast lookups, such as the built-in map, which offers O(1) lookups (that means, you need to do a single operation to find your item, not n operations).
type Box struct {
BoxItems map[int]BoxItem
}
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// Lookup the map by Id
item, ok := box.BoxItems[boxItem.Id]
if ok {
// It exists, do don't create it, just increment
item.Qty++
} else {
item = boxItem
}
// New item so add it to the map
box.BoxItems[boxItem.Id] = item
return item
}
Playground
This is a more correct way to solve your problem.
In the index, value := range someSlice, the value is a fresh new copy of someSlice[index].
package main
import (
"fmt"
)
type BoxItem struct {
Id int
Qty int
}
type Box struct {
BoxItems []BoxItem
}
func (box *Box) AddBoxItem(boxItem BoxItem) BoxItem {
// If the item exists already then increment its qty
for i := range box.BoxItems {
item := &box.BoxItems[i]
if item.Id == boxItem.Id {
item.Qty++
return *item
}
}
// New item so append
box.BoxItems = append(box.BoxItems, boxItem)
return boxItem
}
func main() {
boxItems := []BoxItem{}
box := Box{boxItems}
boxItem := BoxItem{Id: 1, Qty: 1}
// Add this item 3 times its qty should be increased to 3 afterwards
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
box.AddBoxItem(boxItem)
fmt.Println(len(box.BoxItems)) // Prints 1 which is correct
for _, item := range box.BoxItems {
fmt.Println(item.Qty) // Prints 1 when it should print 3
}
}
Playground
Output:
1
3

Resources