slice modification in function

3,121 views
Skip to first unread message

Sotirios Mantziaris

unread,
Apr 10, 2016, 4:47:03 PM4/10/16
to golang-nuts
Hi,

i am trying to write a function that take a slice as a argument and manipulates the slice (removes items).


the example creates a int slice, populates the slice and a function removes all zero elements.
Unfortunately when returning the slice is unharmed and the zero elements are still present.
Pointers did not help either. Is there any special handling necessary?

Thanks

Marvin Stenger

unread,
Apr 10, 2016, 5:02:54 PM4/10/16
to golang-nuts

Caleb Spare

unread,
Apr 10, 2016, 5:05:46 PM4/10/16
to Sotirios Mantziaris, golang-nuts
There's nothing wrong with an "append-style" function like you've
written, where you must assign the result.

However, it's certainly possible to modify the slice if you pass a
pointer: [Marvin sent an example]

It's further easy to modify the slice in-place without making a new one:

https://play.golang.org/p/NZ6RL12a7t
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Alexander Kapshuk

unread,
Apr 11, 2016, 2:53:50 AM4/11/16
to golang-nuts
If a function takes a slice argument, changes it makes to the elements
of the slice will be visible to the caller, analogous to passing a
pointer to the underlying array.
The length of a slice may be changed as long as it still fits within
the limits of the underlying array; just assign it to a slice of
itself.
We must return the slice afterwards because, although Append can
modify the elements of slice, the slice itself (the run-time data
structure holding the pointer, length, and capacity) is passed by
value.
(https://golang.org/doc/effective_go.html#slices)

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

Sotirios Mantziaris

unread,
Apr 11, 2016, 12:40:13 PM4/11/16
to golang-nuts
after many years without pointers... the obvious answer. i did go this way v = &tmp which did not work. thanks

s.mant...@pamediakopes.gr

unread,
Apr 11, 2016, 1:22:48 PM4/11/16
to golang-nuts, smant...@gmail.com
Keep in mind that the role of the function is to remove any zero elements which can be in any place. So the in-place suggestion is not really optimal.

s.mant...@pamediakopes.gr

unread,
Apr 11, 2016, 1:22:49 PM4/11/16
to golang-nuts
thanks, that was obvious.
i did try this one but it failed (not really know why).

s.mant...@pamediakopes.gr

unread,
Apr 11, 2016, 1:22:51 PM4/11/16
to golang-nuts
Without having the best of knowledge here in golang (but a forbidden thing in other languages).
Is it not dangerous to modify a slice when you range over it?

Jan Mercl

unread,
Apr 11, 2016, 1:35:47 PM4/11/16
to s.mant...@pamediakopes.gr, golang-nuts


On Mon, Apr 11, 2016, 19:22 <s.mant...@pamediakopes.gr> wrote:
Without having the best of knowledge here in golang (but a forbidden thing in other languages).
Is it not dangerous to modify a slice when you range over it?

Slice is a value. The range evaluates its argument only once. Any subsequent changes to the slice value cannot change what the range argument evaluated to.

Yet there is a danger. When the range argument is resliced anywhere else, including inside the loop, the program may later mistakenly use more/less elements than the range loop actually processed.
--

-j

Sotirios Mantziaris

unread,
Apr 11, 2016, 2:32:50 PM4/11/16
to golang-nuts, s.mant...@pamediakopes.gr
it seems that i have to learn  more. thanks

Caleb Spare

unread,
Apr 11, 2016, 2:51:54 PM4/11/16
to s.mant...@pamediakopes.gr, golang-nuts, Sotirios Mantziaris
Keep in mind that the role of the function is to remove any zero elements which can be in any place. So the in-place suggestion is not really optimal.

​How so?

Sotirios Mantziaris

unread,
Apr 11, 2016, 4:19:39 PM4/11/16
to golang-nuts, s.mant...@pamediakopes.gr, smant...@gmail.com
discard my comment, did not understand the slice internals correctly. thanks

Bakul Shah

unread,
Apr 11, 2016, 5:08:37 PM4/11/16
to Jan Mercl, golang-nuts
On Mon, 11 Apr 2016 17:35:21 -0000 Jan Mercl <0xj...@gmail.com> wrote:
>
> Slice is a value. The range evaluates its argument only once. Any
> subsequent changes to the slice value cannot change what the range argument
> evaluated to.
>
> Yet there is a danger. When the range argument is resliced anywhere else,
> including inside the loop, the program may later mistakenly use more/less
> elements than the range loop actually processed.

Both appending & reslicing in the loop seems to be safe.
x := []int{0, 1, 2, 3}
for _,v := range x {
fmt.Println(v)
//uncomment one of the three lines below
//if v < len(x) { x = x[v:] }
//x = append(x, v+len(x))
//x = nil
}
Looks like the slice value is cached. Not sure if that is an
implementation or language property.

Map seems to behave differently:

x := map[int]int{0:0, 1:1, 2:2, 3:3}
for k,v := range x {
fmt.Println(k, v)
delete(x,k+1)
}

This prints
0 0
2 2
So it appears a map is not cached. This behavior can be
explained but the following behavior is strange...

x := map[int]int{0:0, 1:1, 2:2, 3:3}
for k,v := range x {
fmt.Println(k, v)
x[len(x)] = len(x)
}

This prints anywhere from 4 to 7 lines. I would have expected
this loop to never terminate (or if cached, iterate exactly
four times).

Caleb Spare

unread,
Apr 11, 2016, 5:18:22 PM4/11/16
to Bakul Shah, Jan Mercl, golang-nuts
This is all well-specified at https://golang.org/ref/spec#For_statements.

The main thing to keep in mind is:

"The range expression is evaluated once before beginning the loop"

So that explains how your slice ranging works.

And for maps:

"The iteration order over maps is not specified and is not guaranteed
to be the same from one iteration to the next. If map entries that
have not yet been reached are removed during iteration, the
corresponding iteration values will not be produced. If map entries
are created during iteration, that entry may be produced during the
iteration or may be skipped. The choice may vary for each entry
created and from one iteration to the next. If the map is nil, the
number of iterations is 0."

Jan Mercl

unread,
Apr 11, 2016, 5:21:42 PM4/11/16
to Bakul Shah, golang-nuts
On Mon, Apr 11, 2016 at 11:08 PM Bakul Shah <ba...@bitblocks.com> wrote:

> Both appending & reslicing in the loop seems to be safe.

That's the value thing I talked about. The bug I meant is the one like in.: http://play.golang.org/p/8c21rN21sZ

John Souvestre

unread,
Apr 11, 2016, 7:53:15 PM4/11/16
to golang-nuts
>> The range expression is evaluated once before beginning the loop, ...

I'm confused about the iteration value. It seems that for arrays it is pre-evaluated while for slices it is not pre-evaluated.

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

John

John Souvestre - New Orleans LA

Matt Harden

unread,
Apr 11, 2016, 7:58:51 PM4/11/16
to John Souvestre, golang-nuts
The value of an array includes all its elements; the value of a slice is effectively a pointer, length and capacity. So range is using a copy of the array and a copy of the slice, but the slice copy can still update elements of the original slice's backing array.

John Souvestre

unread,
Apr 11, 2016, 8:19:22 PM4/11/16
to golang-nuts

OK, I follow what you are saying.  Indeed, for the array case if I use a pointer to the array instead then the do see the “5”.  But this behavior doesn’t seem to match what I read in the Language Specification.

 

John

    John Souvestre - New Orleans LA

 

Reply all
Reply to author
Forward
0 new messages