I am scratching my head over an unexpected difference between having my struct contain list.List vs *list.List. Why doesn't the following work?
type listHolder struct {
id int
mylist list.List
}
func newListHolder(id int, text string) listHolder {
var newLH listHolder
newLH.mylist = *list.New()
newLH.id = id
newLH.mylist.PushBack(text)
return newLH
}
func (l *listHolder) pushBack(text string) {
l.mylist.PushBack(text)
}
func (l *listHolder) printAll() {
for temp := l.mylist.Front(); temp != nil; temp = temp.Next() {
fmt.Println(temp.Value)
}
}
func main() {
a := newListHolder(1, "first")
a.pushBack("second")
fmt.Printf("listHolder %d length %d Front()= %v, Back()=%v
",
a.id, a.mylist.Len(), a.mylist.Front().Value, a.mylist.Back().Value)
a.printAll()
}
This outputs the following, showing that the length is as expected but the Front() and Back() methods don't work.
listHolder 1 length 2 Front()= `<nil>`, Back()=<nil>
<nil>
If I define the struct as
// Same thing with a pointer
type listHolderPtr struct {
id int
mylist *list.List
}
func newListHolderPtr(id int, text string) listHolderPtr {
var newLH listHolderPtr
newLH.mylist = list.New()
newLH.id = id
newLH.mylist.PushBack(text)
return newLH
}
that works as expected, but of course, any copies of the listHolder struct share a reference to the same list, which is not what I want. I need to be able to copy the surrounding object and get a new copy of the internal list. Is that possible?
See https://play.golang.org/p/KCtTwuvaS1R for a simplified example of what I'm trying to do. In the real use case, I'll be pushing onto the back and popping off the front of each listHolder in a slice of listHolder in a complicated nested loop.
I think @JimB's suggestion to make a local copy of your list whenever you're pushing a value to listHolder.mylist
could be a good solution to your problem (given, that I understand the underlying issue correctly). I tried to come up with an implementation that looks like the following:
package main
import (
"container/list"
"fmt"
)
type listHolder struct {
id int
mylist list.List
}
func newListHolder(id int) listHolder { // don't push back when constructing a new listHolder
var newLH listHolder
newLH.mylist = *list.New()
newLH.id = id
return newLH
}
func (l *listHolder) pushBack(text string) {
// create a temporary list to copy all old and the new value to
tmpList := list.New()
// copy all existing values from l.mylist
for e := l.mylist.Front(); e != nil; e = e.Next() {
fmt.Printf("pushing back '%v' from old list
", e.Value)
tmpList.PushBack(e.Value)
}
// push back the new value
tmpList.PushBack(text)
// print the new tmpList for debugging purposes
for ele := tmpList.Front(); ele != nil; ele = ele.Next() {
fmt.Printf("creating new list element: %v
", ele.Value)
}
// replace l.mylist with tmpList
l.mylist = *tmpList
// another version of this solution could be to return a new (i.e. copied)
// *listHolder with all the old values and the new 'text' value
}
func (l *listHolder) printAll() {
for temp := l.mylist.Front(); temp != nil; temp = temp.Next() {
fmt.Println(temp.Value)
}
}
func main() {
a := newListHolder(1)
a.pushBack("first") // push a value to a
a.pushBack("second") // push another value to a
fmt.Printf("listHolder %d length %d Front()=%v, Back()=%v
",
a.id, a.mylist.Len(), a.mylist.Front().Value, a.mylist.Back().Value)
a.printAll()
}
This code outputs:
creating new list element: first // nothing to copy, only creating a new list element
pushing back 'first' from old list // copy element ...
creating new list element: first // ... from old list
creating new list element: second // and push new element to 'tmpList'
listHolder 1 length 2 Front()=first, Back()=second // print a summary
first // of the
second // new list
If I had some mock data, I could do some more testing/debugging. At least this code works without a *list.List
.