I started learning Go and stumbled upon this Go Tour exercise of implementing a singly linked list. The only functions I implemented are an AddItem
and a Print
and this is the solution I had in mind.
package main
import (
"fmt"
)
// List represents a singly-linked list that holds
// values of any type.
type List[T any] struct {
next *List[T]
val T
}
func (li *List[T]) AddItem(val T) *List[T] {
// fmt.Println("Current:", li)
li.next = &List[T]{
next: nil,
val: val,
}
li = li.next // * This is not updating unless I do a return
// fmt.Println("Next:", li)
return li
}
func Print[T any](head *List[T]) {
i := 0
itr := head
for itr != nil {
fmt.Printf("(idx: %d, val: %d) next: %v\n", i, itr.val, itr.next)
i++
itr = itr.next
}
}
func main() {
ll := &List[int]{
next: nil,
val: 7,
}
head := ll
ll = ll.AddItem(2)
ll = ll.AddItem(3)
ll = ll.AddItem(6)
Print(head)
}
// OUTPUT (expected)
(idx: 0, val: 7) next: &{0xc000014270 2}
(idx: 1, val: 2) next: &{0xc000014280 3}
(idx: 2, val: 3) next: &{<nil> 6}
(idx: 3, val: 6) next: <nil>
The above function is working as intended. However, directly updating the receiver in the marked line instead of doing a return is where things go wrong, like this:
func (li *List[T]) AddItem(val T) {
// fmt.Println("Current:", li)
li.next = &List[T]{
next: nil,
val: val,
}
li = li.next // * This is expected to update `ll` in main itself but it does not
fmt.Println("Next:", li)// * This print is actually correct
}
func main() {
...
ll.AddItem(2)
ll.AddItem(3)
ll.AddItem(6)
Print(head)
}
// OUTPUT (unexpected)
(idx: 0, val: 7) next: &{<nil> 6}
(idx: 1, val: 6) next: <nil>
However this does not work and ll
always points to the first value.
I found similar issues such as this one, this and this. Based on that, I tried directly updating the values and also using double pointers. Am I confusing something?