I'm doing a micro-optimisation of my LRU cache solution in Golang where I'm using https://golang.org/pkg/container/list/. My solution works by having a map[int]*list.Element
, where each list.List
list.Element
is []int
, with [0]
being key, and [1]
being value.
I'm trying to move from []int
to [2]int
for my optimisation, but I'm then running into the issue that modifying the fixed-size array, after ee := e.Value.([2]int)
(note the [2]int
type for fixed-size array), is no longer modifying the underlying values in the list.Element
, unlike was the case w/ ee := e.Value.([]int)
(note the []int
type), which I guess makes perfect sense, since slices are based on references, whereas fixed-size arrays are based on copied values.
I've tried stuff like e.Value.([2]int)[1] = …
, as well as various combinations with := &e.Value…
, and casting from [2]int
to []int
, but it all results in complier errors.
Q: Is there no way to use the container/list
with an embedded array, and perform modifications of said fixed-size array in-place?
As you already noted:
I guess makes perfect sense, since slices are based on references, whereas fixed-size arrays are based on copied values
So if you want to make this work, you'll need to use references to your fixed-size arrays by storing pointers to the arrays instead of array values.
That way, you'll be able to modify the underlying array through the list element.
See here for a simple example:
package main
import (
"container/list"
"fmt"
)
func main() {
l := list.New()
// create a fixed size array and initialize it
var arr [2]int
arr[0] = 1
arr[1] = 2
// push a pointer to the array into the list
elem := l.PushFront(&arr)
// modify the stored array
elem.Value.(*[2]int)[0] = 3
// print the element from iterating the list
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
// print the underlying array, both are modified
fmt.Println(arr)
}
EDIT
Note that this behaviour is not something specific to this list implementation, but rather related how type assertions work in the language itself.
See here:
https://golang.org/doc/effective_go.html#interface_conversions
Quoting from that section (and adding emphasis of my own):
The syntax borrows from the clause opening a type switch, but with an explicit type rather than the type keyword:
value.(typeName)
and the result is a new value with the static type typeName.
When using reference types, copying the value does not affect you cause copying a pointer value ends up allowing you to change the same underlying reference.
But when the array is a value itself, it does not make sense to assign to the copy. When you try to modify the array directly (without assigning the type assertion to a variable) Go would even catch this at compile time so that you don't assign to this "temporary" copy which would obviously be a mistake. That's how you got all your syntax errors when trying to do this.
To overcome this (if you don't want to use pointers) one possibility might be for you to implement your own list
, borrowing from the implementation you are using but making your Element
's value an explicit [2]int
instead of an interface.
That would remove the need to make the type assertion and you'll be able to modify the underlying array.