Sam
Sam

Reputation: 765

Copying a struct's slice parameter in Go

Let's say I have a struct with a slice parameter like the following, and I create one and fill it with some values:

type s struct {
  sl []float32
}

func NewS() *s {
  return &s{
    sl: make([]float32, 3),
  }
}

func main() {
  a := NewS()
  a.sl[0] = 1
  a.sl[1] = 2
  a.sl[2] = 3

  b := NewS()
  // Code here
}

I want b to have an sl with the same values as a.sl, and in particular, I'd like to understand how to do it in different ways:

  1. Where the entire b points to the entire a (so that if there were other parameters in the struct, those would all match)
  2. Where b.sl would point to a.sl
  3. As a deep copy, where there are no pointers (and done not by iterating through the slice, but by a way that's repeatable regardless of type, ideally)

What follows is what I've tried and found so far.

b = a
fmt.Printf("a: %p\n", &a)
fmt.Printf("b: %p\n", &b)
fmt.Printf("a.sl: %p\n", &a.sl)
fmt.Printf("b.sl: %p\n", &b.sl)

Which outputs different memory addresses for the structs, but the same memory addresses for the slices. Why is that? Why wouldn't the struct addresses be the same, too, if a is just pointing to b?

a: 0xc00000e030
b: 0xc00000e038
a.sl: 0xc000054040
b.sl: 0xc000054040

Then I tried:

b.sl = a.sl
// ...

Which outputs all distinct memory addresses. It makes sense of course that the address of a and b are different, but why isn't b.sl pointing to the address of a.sl?

a: 0xc00000e028
b: 0xc00000e030
a.sl: 0xc00000c030
b.sl: 0xc00000c048

And finally I tried:

copy(b.sl, a.sl)
// ...

Which outputs similarly to the above. This result is the only one I expected, since I expected this to make a deep copy.

a: 0xc0000b8018
b: 0xc0000b8020
a.sl: 0xc0000ae018
b.sl: 0xc0000ae030

Could you help me understand what's happening in these 3 cases and other ways to achieve my desire results and perform various types of copies?

Upvotes: 1

Views: 1741

Answers (2)

user13631587
user13631587

Reputation:

Here's some background information: A slice contains a pointer to the slice's backing array, the length of the slice and the capacity of the backing array. The (pointer, length, capacity) is referred to as a slice header.

The address of a slice is the address of the slice header, not the pointer to the slice's backing array or the first element of the slice's backing array.

b = a

The variables a and b point to the same value after the statement is executed.

The variables a and b are distinct and therefore have different addresses.

The expression &a.sl is the address of the sl field in the pointed at value. The expression &b.sl equals &a.sl because a and b point at the same value.

This is your #1: a an b point at the same value.

b.sl = a.sl

The statement copies the slice header from a.sl to b.sl. The slices have different addresses because slice headers remain distinct.

This is close to your #2: a.sl an b.sl point at the same backing array. A modification to an element in a.sl is visible through b.sl, but changes to the slice header a.sl are not reflected in `b.sl.

copy(b.sl, a.sl)

The statement copies the elements in a.sls backing array to b.sl.

This is your #3, a deep copy. Changes through a are not reflected in b.

Upvotes: 2

Burak Serdar
Burak Serdar

Reputation: 51587

A slice is a triple containing a pointer to the underlying array, length, and capacity. When you assign a slice to another slice, you assign that triple. The underlying array remains the same.

A slice is a view of an array. If you need a deep copy of a slice, you have to create another slice and copy it yourself.

If you assign an array to another, that copies each element of the source to the target.

Upvotes: 1

Related Questions