Thanks for your reply, Ian. I've decided on using a []*Foo now. Just to clarify, however: In general, copying/assigning from a struct literal as in a[i] = Foo{}, is that always safe for these sync types? Or generally, is copying their "untouched" zero-values always safe?
Antonio On Sunday, August 27, 2023 at 7:05:15 PM UTC+2 Ian Lance Taylor wrote: > On Sun, Aug 27, 2023 at 7:54 AM Antonio Caceres Cabrera > <julioca...@gmail.com> wrote: > > > > Go vet complains about this minimal example code > > > > type Foo struct { > > val atomic.Uint64 > > } > > > > func main() { > > var foos = make([]Foo, 0) > > var bar Foo > > bar.val.Store(5) > > foos = append(foos, bar) // call of append copies lock value: > example.com/foo.Foo contains sync/atomic.Uint64 contains > sync/atomic.noCopy > > > > } > > > > Because the types of the sync package are not supposed to be copied. > > This is also true for sync.Map, sync.Mutex, sync.WaitGroup etc. > > > > However, if I instead copy in a zero-value Foo and then set that, go vet > does not complain, even if later appends are done: > > > > func main() { > > var foos = make([]Foo, 0) > > foos = append(foos, Foo{}) > > foos[0].val.Store(5) > > foos = append(foos, Foo{}) > > foos = append(foos, Foo{}) > > // append some more > > // ... > > fmt.Println(foos) > > } > > > > However, is this supposed to be safe, or is go vet just not catching it? > > Even if copying a zero-value of such a type is safe, append() might have > to re-allocate the underlying array, which includes copying the existing > values, which might already have been used, as foo[0] has been in this > case. This would violate the noCopy trait of the type. My question is thus: > > > > Is it safe to keep nocopy types from the sync and sync.atomic packages > in a slice, given that they might internally be copied when the slice is > appended to? > > > > Of course, copying these types is not by itself atomic or synchronized > with other accesses. So let's assume that I, as the programmer, guarantee > that while an append happens, this slice will only be accessed by the one > appending goroutine, using other synchronization primitives, such as mutex, > channels etc. or by only running these appends in the main before any other > go-routine is started. > > Doing an append on a slice of types that contain atomic types is in > general not safe and go vet is not catching it. > > If your program can guarantee that while the append is happening there > is no concurrent access to the atomic types, then I think that is > safe. It's not safe in general for all nocopy types, but it's safe > for types like atomic.Uint64. It doesn't seem like a very clear way > to write a program though. It may be better to use a slice of > pointers: in your example, []*Foo. Or precount the size of the slice > to ensure that append won't copy it. > > Ian > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/6ad297ba-19d4-4eaf-a2c2-252ca9db390cn%40googlegroups.com.