Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Should Go 2.0 support slice comparison?

Should Go 2.0 support slice comparison?

In the same way it is useful to compare pointers (and not their values), I came across a situation where I wanted to compare slices (and not their backing array's values), as if they were structs containing ptr, len and cap. Thus slices a == b, would be true iff a.ptr == b.ptr && a.len == b.len && a.cap == b.cap, regardless of what is contained in a or b's backing arrays. Lastly, since equality and inequality would be clearly defined, I could use slices as map keys, as changing the array data would be safe and have no impact on the keys themselves.

I was writing some temporary code that would proxy requests to an old system for third party communication drivers that hadn't yet been re-written into the new system. Requests from clients would be split out to the different drivers, but the request data slices would be shared between drivers.

Each request looked something like this:

[updateData1][driver1][driver2][driver3]:[updateData2][driver1][driver3]:[updateData3][driver2][driver3]

However inside the system they were divided per driver:

[driver1][updateData1][updateData2]

[driver2][updateData1][updateData3]

[driver3][updateData1][updateData2][updateData3]

Or in Go:

type data []int

type driverReq struct { // [driver1][updateData1][updateData2]
	info Driver
	updates []data
}

The plan was to bundle up the unsupported split out requests, and merge them into a single request for proxying.

Now say driver2 and driver3 were not yet written into the new system. I could just take the data structure above and create the following request:

[updateData1][driver2]:[updateData3][driver2]:[updateData1][driver3]:[updateData2][driver3]:[updateData3][driver3]

Note that updateData1 and updateData3 are unnecessarily duplicated. In some cases the data slices would be very long and used by many drivers, making the resulting proxied requests undesirably long. Ideally I wanted to share duplicate slices the same way the data was originally received, and thus be left with:

[updateData1][driver2][driver3]:[updateData2][driver3]:[updateData3][driver2][driver3]

Thus I needed to find the slices that were referencing the same data. Now, I could have compared all the values in each backing array, but with the data being quite long, and as identical slices would be referencing the same memory location, that seemed cumbersome and inefficient.

If slices were comparable I could have done:

type proxyReq map[data][]driver // <-- Not currently possible as type data is a slice
func (p proxyReq) Output() string { ... }

pr := make(proxyReq,2)
for _, dr := range driverReqs {
	z:
	for _, u := range dr.updates {
		for i,v := range pr {
			if i == u { // <-- Not currently possible as slices cannot be compared
				pr[i] = append(pr[i],dr.info)
				continue z
			}
		}
		pr[u] = []driver{dr.info}
	}
}

Alas, I ended up comparing the memory location of the first element in each slice.

type proxyDriverData struct {
	data []int
	drivers []driver
}

type proxyReq map[*int][]driver // <-- Where *int is the memory ptr to the first value in each slice

func (p proxyReq) Output() string { ... }

pr := make(proxyReq,2)
for _, dr := range driverReqs {
	z:
	for _, u := range dr.updates {
		for i,v := range pr {
			if i == &u[0] { // <-- First element memory ptr comparison
				pr[i].drivers= append(pr[i].drivers,dr.info)
				continue z
			}
		}
		pr[&u[0]] = proxyDriverData{data: u, drivers: []driver{dr.info}}
	}
}

I've read that some consider comparing slices should stay banned because new users may think that the underlying array would be compared, not the slice struct's values. I'd argue that this reasoning isn't valid on the basis that we don't worry about this problem with pointers.

Thanks for your time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.