How to return slice by reference? - go

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.

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.

Modify slice element using pointer

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]

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.

Delete element in a slice and returning the removed one and the remaining

I just want a function that having a slice of a struct type "t", returns the returns the element I'm looking for and the remaining, I tried with the partial solution for my problem like pointed out here:
Delete element in a slice
But for a weird reason, it does not work as expected
https://play.golang.org/p/tvJwkF5c_tj
func main() {
var names = []string{"john", "julio", "pepito","carlos"}
fmt.Println(getMe("john", names))
}
func getMe(me string, names []string) (string, []string, bool) {
for i := range names {
if names[i] == me {
return names[i], append(names[:i], names[i+1:]...), true
}
}
return "", nil, false
}
but the result gives me:
julio [julio pepito carlos] true
UPDATE:
https://play.golang.org/p/1xbu01rOiMg
Taking the answer from #Ullaakut
If I do: append(names[:i], names[i+1:]...), it changes the original slice, so this does not work for me, I do not want my slice to change, because I will be using it later on
Simply use the range to get both the value and the index, instead of accessing the value by using the index.
package main
import (
"fmt"
)
func main() {
var names = []string{"john", "julio", "pepito", "carlos"}
name, newNames, _ := getMe("john", names)
fmt.Println("extracted name:\t\t\t\t", name)
fmt.Println("new slice without extracted name:\t", newNames)
fmt.Println("old slice still intact:\t\t\t", names)
}
func getMe(me string, names []string) (string, []string, bool) {
var newSlice []string
for i := 0; i < len(names); i++ {
if names[i] == me {
newSlice = append(newSlice, names[:i]...)
newSlice = append(newSlice, names[i+1:]...)
return names[i], newSlice, true
}
}
return "", nil, false
}
Outputs
extracted name: john
new slice without extracted name: [julio pepito carlos]
old slice still intact: [john julio pepito carlos]
See playground example
Edit after request for a faster version: Using the manual for instead of the range loop is much faster. Since you need to create a new slice without the element, it's necessary to build a new slice within the function, which is always going to take some processing power.

How to remove an item from a slice by calling a method on the slice

Go has stumped me again. Hopefully someone can help. I've created a slice (mySlice) that contains pointers to structs (myStruct).
The problem is the "Remove" method. When we're inside "Remove" everything is fine, but once we return, the slice size hasn't changed, and so we see the last element listed twice.
I originally tried writing "Remove" using the same pattern used in the "Add" method, but it wouldn't compile and has been commented out.
I can get it to work by returning the newly created slice to the calling function, but I don't want to do this because mySlice (ms) is a singleton.
And if I hadn't asked enough already...
The code for the "Add" method is working, although I'm not sure how. From what I can gather "Add" is receiving a pointer to the slice header (the 3 item "struct"). From what I've read, the length and capacity of an slice don't get passed to methods (when passing by value), so perhaps passing a pointer to the slice allows the method to see and use the length and capacity thereby allowing us to "append". If this is true, then why doesn't the same pattern work in "Remove"?
Thanks very much for everyone's insights and help!
package main
import (
"fmt"
)
type myStruct struct {
a int
}
type mySlice []*myStruct
func (slc *mySlice) Add(str *myStruct) {
*slc = append(*slc, str)
}
//does not compile with reason: cannot slice slc (type *mySlice)
//func (slc *mySlice) Remove1(item int) {
// *slc = append(*slc[:item], *slc[item+1:]...)
//}
func (slc mySlice) Remove(item int) {
slc = append(slc[:item], slc[item+1:]...)
fmt.Printf("Inside Remove = %s\n", slc)
}
func main() {
ms := make(mySlice, 0)
ms.Add(&myStruct{0})
ms.Add(&myStruct{1})
ms.Add(&myStruct{2})
fmt.Printf("Before Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms)
ms.Remove(1) //remove element 1 (which also has a value of 1)
fmt.Printf("After Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms)
}
and the results...
Before Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{1}) %!s(*main.myStruct=&{2})]
Inside Remove = [%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2})]
After Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2}) %!s(*main.myStruct=&{2})]
You were right the first time with Remove1(). Remove gets a copy of the slice and therefore cannot change the length of the slice.
The issue in your remove function is that according to order of operations in Go, slicing comes before dereferencing.
The fix is to change *slc = append(*slc[:item], *slc[item+1:]...) to *slc = append((*slc)[:item], (*slc)[item+1:]...).
However I would recommend the following for readability and maintainability:
func (slc *mySlice) Remove1(item int) {
s := *slc
s = append(s[:item], s[item+1:]...)
*slc = s
}
Because append would not necessarily return the same address of reference to the slice, as Stephen Weinberg has pointed out.
Another way to workaround with this limitation is defining a struct that wraps the slice.
for example:
package main
import "fmt"
type IntList struct {
intlist []int
}
func (il *IntList) Pop() {
if len(il.intlist) == 0 { return }
il.intlist = il.intlist[:len(il.intlist)-1]
}
func (il *IntList) Add(i... int) {
il.intlist = append(il.intlist, i...)
}
func (il *IntList) String() string {
return fmt.Sprintf("%#v",il.intlist)
}
func main() {
intlist := &IntList{[]int{1,2,3}}
fmt.Println(intlist)
intlist.Pop()
fmt.Println(intlist)
intlist.Add([]int{4,5,6}...)
fmt.Println(intlist)
}
output:
[]int{1, 2, 3}
[]int{1, 2}
[]int{1, 2, 4, 5, 6}

Resources