gnolang / gno

Gno: An interpreted, stack-based Go virtual machine to build succinct and composable apps + Gno.land: a blockchain for timeless code and fair open-source.
https://gno.land/
Other
892 stars 371 forks source link

Values of slice not being copied properly by reference #1796

Closed leohhhn closed 7 months ago

leohhhn commented 7 months ago

Description

I believe I found an issue in the GnoVM, while writing the Memeland realm. With the following code:

type Post struct {
    ID            string
    Data          string
    Author        std.Address
    Timestamp     time.Time
    UpvoteTracker *avl.Tree // address > struct{}{}
}

type Memeland struct {
    Posts       []*Post
}

When making a pointer copy of m.Posts, and updating that value, the m.Posts slice does not get updated.


posts := m.Posts // copy pointer to m.Posts slice

for i, post := range posts {
    // deleting a specific element
        if post.ID == id {
        posts = append(posts[:i], posts[i+1:]...)
                // `posts` will be updated, m.Posts will not
        return id
    }
}
notJoon commented 7 months ago

Assuming the behavior in the Gno is identical to that of the Go language for convenience.

In Go, slices are fundamentally reference types. When using posts := m.Posts, both posts and m.Posts point to the same data. But they operate as separate references (i.e., independent slices). This means that while metadata (length or capacity) of the slice is copied, the content of the data array they internally point to is shared.

Therefore, deleting an element from the posts slice does not affect the m.Posts slice because although both slices access the same data array, each slice has different metadata. Due to these characteristics, changes made to posts are not reflected in m.Posts IMHO.

Here's my reproduction code:

  1. change on posts
  2. directly change on m.Posts
deelawn commented 7 months ago

I think we can close this as intended behavior. @notJoon is mostly correct -- but many cases modifying a single slice element will affect other slices that reference the same underlying array, and this is intended to mirror the go functionality. Slices will continue to use the same underlying array until the array needs to grown / reallocated.

Here is an example: https://play.gno.land/p/zSEYhS76ym3

@leohhhn please close if you agree.

leohhhn commented 7 months ago

Looking into this in more detail, it is actually intended behavior. Closing this. Thanks @notJoon @deelawn