Nil / empty slice equality

6,557 views
Skip to first unread message

ziutek

unread,
Nov 17, 2011, 7:53:13 AM11/17/11
to golang-nuts
var n, e []byte

n = nil
e = []byte{}

reflect.DeepEqual(n, e) returns true in last weekly release but false
in tip

Archos

unread,
Nov 17, 2011, 8:01:45 AM11/17/11
to golang-nuts

Ian Lance Taylor

unread,
Nov 17, 2011, 1:11:25 PM11/17/11
to ziutek, golang-nuts
ziutek <ziu...@Lnet.pl> writes:

There was a bug, and it has been fixed. A nil slice is not the same as
a slice which exists but is empty.

Ian

ziutek

unread,
Nov 17, 2011, 2:37:02 PM11/17/11
to golang-nuts
Is this intentionally difference?

len(n) == 0 and len(e) == 0
cap(n) == 0 and cap(e) == 0

append(e, 1) and append(n, 1) work the same way. It seems that n and e
are equal but n == nil is true and e == nil is false.

It seems that we have two subtly different classes of empty slice. Is
this behavior somewhere really useful?

Rob 'Commander' Pike

unread,
Nov 17, 2011, 2:38:35 PM11/17/11
to ziutek, golang-nuts
An empty cup is not the same as a missing cup.

-rob

roger peppe

unread,
Nov 17, 2011, 2:48:16 PM11/17/11
to Rob 'Commander' Pike, ziutek, golang-nuts
unless the cup is happens to be a string :-)

Rob 'Commander' Pike

unread,
Nov 17, 2011, 2:50:54 PM11/17/11
to roger peppe, ziutek, golang-nuts

On Nov 17, 2011, at 11:48 AM, roger peppe wrote:

> unless the cup is happens to be a string :-)

An empty slice can potentially be grown without allocation. A nil slice can never be grown without allocation.

A string can never be grown at all.

-rob


ziutek

unread,
Nov 17, 2011, 4:17:15 PM11/17/11
to golang-nuts
On 17 Lis, 20:38, Rob 'Commander' Pike <r...@google.com> wrote:
> An empty cup is not the same as a missing cup.

cap(e) == 0 and cap(n) == 0 so my empty cap is empty and my missing
cup is empty to!
I can use my missing cup the same way as my empty cup. Isn't it seem a
little strange?

Brad Fitzpatrick

unread,
Nov 17, 2011, 4:25:18 PM11/17/11
to ziutek, golang-nuts
who cares about cups if you don't have any liquid.

Gustavo Niemeyer

unread,
Nov 17, 2011, 4:42:43 PM11/17/11
to ziutek, golang-nuts
> I can use my missing cup the same way as my empty cup. Isn't it seem a little strange?

You're not using the cup. You're asking the amount of stuff in the
cup, which is the same for a missing or an empty cup.

Analogies aside, in practice this is brilliant. Many algorithms can be
simplified when len and cap don't panic on nil slices and maps.

--
Gustavo Niemeyer
http://niemeyer.net
http://niemeyer.net/plus
http://niemeyer.net/twitter
http://niemeyer.net/blog

-- I'm not absolutely sure of anything.

ziutek

unread,
Nov 17, 2011, 5:07:09 PM11/17/11
to golang-nuts
> You're not using the cup. You're asking the amount of stuff in the
> cup, which is the same for a missing or an empty cup.
>
> Analogies aside, in practice this is brilliant. Many algorithms can be
> simplified when len and cap don't panic on nil slices and maps.

copy and append don't panic to and it is ok. But I wonder if the
existence of this subtly difference between empty and nil slice is
more useful or harmful to the programmer.

DisposaBoy

unread,
Nov 18, 2011, 3:06:20 AM11/18/11
to golan...@googlegroups.com
please correct me if my assumptions are incorrect but if the cap is zero then regardless of what the variable holds, you're going to have initialize it with something useful either directly with make or indirectly via append, etc before you can use it anyway so afaics the difference shouldn't matter too much in practice

ziutek

unread,
Nov 18, 2011, 4:20:16 AM11/18/11
to golang-nuts
One problem which occurs in my code was new (fixed) behavior of
reflect.DeepEqual because uninitialized and empty slices are now
distinguished. This wasn't a big problem for me but i should debug my
code one hour or more to fix it.

yy

unread,
Nov 18, 2011, 4:53:04 AM11/18/11
to ziutek, golang-nuts
2011/11/18 ziutek <ziu...@lnet.pl>:

You may want to distinguish an uninitialized slice from a slice which
has been used and re-sliced until the end. eg:

package main

import(
"fmt"
"reflect"
)

func main() {
var a, b []int
// use the slice a
a = append(a, 1)
a = a[1:]
fmt.Println(reflect.DeepEqual(a,b))
}

I would find surprising that this program printed "true", as it was
happening. In my opinion it is clear that it was a bug.

The current (fixed) behavior can be useful to differentiate an
uninitialized list (nil: empty cup) from a list which has already been
used (a[len(a):]: I don't know if the cup is empty or full anymore,
but I'm quite sure it is not deeply equal to the missing cup).


--
- yiyus || JGL .

ziutek

unread,
Nov 18, 2011, 5:49:34 AM11/18/11
to golang-nuts
On 18 Lis, 10:53, yy <yiyu....@gmail.com> wrote:
>         var a, b []int
>         // use the slice a
>         a = append(a, 1)
>         a = a[1:]
>         fmt.Println(reflect.DeepEqual(a,b))
>
> I would find surprising that this program printed "true", as it was
> happening. In my opinion it is clear that it was a bug.

I check this in tip and it prints "true".

I agree that if reflect.DeepEqual distinguishes between empty and nil
slice it should distinguish between two slices with different capacity
to. But if it distinguishes only on the basis of visible content of
slice, then any two zero-length slices of the same type should be
treated as equal, even if one of them is nil.

akshay...@gmail.com

unread,
Jul 1, 2015, 7:38:18 PM7/1/15
to golan...@googlegroups.com
This thread is quite old now but I found the discussion quite useful since I ran into the issue myself. I feel that a mention of this in the docs for 'builtin' might be useful since the only other place it is referenced is the blog post on slice internals (http://blog.golang.org/go-slices-usage-and-internals). I feel that this warrants a more formal entry. Thoughts?
Reply all
Reply to author
Forward
0 new messages