0

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?

usersina
  • 1,063
  • 11
  • 28
  • 1
    To update the value to which the receiver points, you'd have to do: `*li = *li.next`. It is known. – mkopriva Feb 01 '23 at 08:40
  • Trying that simply outputs `(idx: 0, val: 6) next: ` which is not the correct output – usersina Feb 01 '23 at 08:48
  • That's because your algo is wrong. My previous comment only answers the question in the title of your post. – mkopriva Feb 01 '23 at 08:59
  • 2
    If you're willing to "re-model" the data structure to fit the chosen algo, then you can do it by "taking the other direction" (i.e., instead of head and next, you should do it with tail and prev). In other words you can use your approach to "prepend" but not to "append" items (see [example](https://go.dev/play/p/mDOsuwvJTJZ)). – mkopriva Feb 01 '23 at 09:30
  • 1
    Oh okay, it makes sense now. Thanks for clearing it up. – usersina Feb 01 '23 at 11:01

0 Answers0