Rubber Ducky Moments: 'Completely unrelated []interface{}!!!'

I mentioned on Twitter earlier today that very often when I get really stuck on something and finally decide to ask for help on a forum, the answer suddenly hits me as I finish writing my post. Most of the time the problem is something really silly that I just kept completely overlooking, and the answer feels painfully obvious once it clicks. Despite the fact that these might be a little embarrassing to post, I figured rather than letting my entire would-be forum post go to total waste once I finally get what’s happening (and so never end up clicking the “Post” button), it might be nice to post the issue on my blog, if nothing else as a reminder of my own facepalm moments.

Here is one such story from today. After-the-fact comments are added in [square braces].

“Completely unrelated []interface{}!!!”

Hi,

I feel like I’m either sleep deprived or taking crazy pills and would appreciate some help explaining what’s happening here. Here are the two methods involved:

    func (a *AggregateAge1685) SetElements(elements []interface {})  {
		for _, e := range a.Elements {
			a.RemoveElement(e)
		}
		for _, e := range elements {
			a.AddElement(e)
		}
	}

   
    func (a *AggregateAge1685) RemoveElement(e interface{}) {
        uid := e.(string)
    	for idx, id := range a.Elements {
		if id == uid {
	    		a.Elements = append(a.Elements[:idx], a.Elements[idx+1:]...)
	    		break
    		}
    	}
    }

[As an aside, these two methods are generated by my self modifying simulation experiment!]

Here is what is happening: I pass a slice of two unique elements to SetElements. Eg: []interface{"5,0","7,1"}

When the first element is removed via a.RemoveElement(e), the slice passed to SetElements() changes to []interface{"7,1","7,1"}.

I cannot see why the removal of an element in the a.Elements field is modifying the unrelated elements slice. What obvious thing am I missing here?

Answer

The answer, of course, is that my “unrelated” elements slice is far from unrelated. I used to copy the contents of a.Elements into a new slice with a new backing array and modify the new slice, then pass it back to the Aggregate struct to replace the old elements with the new ones. Last week I changed this (wrongly) to retrieve a.Elements and then modify a.Elements…and then forgot about it. Passing a slice by value in Go passes the slice header, but the slice value does not include the elements. The slice header includes the slice capacity, length, and a pointer to the backing array. So I’ve really been acting on the same underlying array in my two []interface{}s.

Here is a useful link with more information about Go slices: https://blog.golang.org/go-slices-usage-and-internals

comments powered by Disqus