TBQH the word "mutable" doesn't make a lot of sense in Go (even though the spec also calls strings "immutable"). Arguably, *all* values in Go are immutable. It's just that all *pointers* in Go allow to modify the referenced variables - and some types allow you to get a pointer to a shared variable, which strings don't.
That is, a `[]byte` is immutable - you have to write `x = append(x, v)` specifically because `append` creates a new slice value and overwrites the variable `x` with it. However, a `[]byte` refers to an underlying array and `&b[0]` allows you to obtain a pointer to that underlying array. So a `[]byte` represents a reference and that reference allows to mutate the referenced storage location. The same goes for a `*T`, a `map[K]V`, or a `type S struct{ X int; P *int }` - `S` itself is immutable, but `S.X` is a reference to some potentially shared variable. A `string` meanwhile, does not allow you to obtain a pointer to the underlying storage and that's what makes it "immutable". And that does indeed mean that if you pass a `string` value around, that can't lead to data races, while passing a `[]byte` around *might*. But for this case, it doesn't really matter whether or not the field is a `string` or a `[]byte` or an `int`: Because the "mutable" type is the `*URL`. Which represents a reference to some underlying `URL` variable, that you can then mutate. The race happens because you have a method on a pointer that mutates a field - *regardless* of the type of that field. I don't know if that helps, it's a bit subtle. On Mon, Mar 25, 2024 at 1:35 PM 'Lirong Wang' via golang-nuts < golang-nuts@googlegroups.com> wrote: > Wow, i am from other language and i thought `string` is immutable or > something like that, so thread-safe for this operation. learned something > new!!! Thanks > On Thursday, March 21, 2024 at 11:42:24 PM UTC+8 Ethan Reesor wrote: > >> I hadn't used the race detector before. I do see a race warning for >> (*URL).String() among an embarrassing number of other results. I'm going to >> update (*URL).String() to use atomic.Pointer to remove the race. >> >> Thanks, >> Ethan >> >> On Thu, Mar 21, 2024 at 8:59 AM 'Axel Wagner' via golang-nuts < >> golan...@googlegroups.com> wrote: >> >>> On Thu, Mar 21, 2024 at 2:48 PM 王李荣 <wanglir...@gmail.com> wrote: >>> >>>> hi Axel, >>>> >>>> is not modifying `u.memoize.str` thread-safe? the len and the data >>>> point should become visible at same time? >>>> >>> >>> What makes you think that? To be clear, there are no benign data races. >>> Even a data-race on a variable smaller than a word is still a data-race, >>> unless you do it holding a lock or using atomic instructions. But strings >>> are *larger* than single words. >>> >>> To demonstrate that the effect I am talking about is real, look at this >>> code: https://go.dev/play/p/LzRq9-OH-Xb >>> >>> >>>> >>>> 在2024年3月16日星期六 UTC+8 06:29:06<Axel Wagner> 写道: >>>> >>>>> Have you tried running the code with the race detector enabled? I >>>>> suspect that you are concurrently modifying `u.memoize.str` by calling >>>>> `u.String()` from multiple goroutines. And the non-zero length of the >>>>> string header written by one goroutine becomes visible to the other one, >>>>> before the modification to the data pointer. >>>>> >>>>> On Fri, Mar 15, 2024 at 11:15 PM Ethan Reesor <ethan....@gmail.com> >>>>> wrote: >>>>> >>>>>> From this CI job >>>>>> <https://gitlab.com/accumulatenetwork/accumulate/-/jobs/6398114923>: >>>>>> >>>>>> panic: runtime error: invalid memory address or nil pointer >>>>>> dereference >>>>>> [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x51d8b7] >>>>>> goroutine 1589381 [running]: >>>>>> strings.EqualFold({0xc000beec20?, 0x0?}, {0x0?, 0xacace7?}) >>>>>> /usr/local/go/src/strings/strings.go:1111 +0x37 >>>>>> >>>>>> gitlab.com/accumulatenetwork/accumulate/pkg/url.(*URL).Equal(0xc000a74e40?, >>>>>> 0xc00094c540) >>>>>> /builds/accumulatenetwork/accumulate/pkg/url/url.go:472 +0x10c >>>>>> >>>>>> This is in a docker container based on the go:1.22 image, so the >>>>>> panic appears to be happening here: >>>>>> >>>>>> func EqualFold(s, t string) bool { >>>>>> // ASCII fast path >>>>>> i := 0 >>>>>> for ; i < len(s) && i < len(t); i++ { >>>>>> sr := s[i] >>>>>> tr := t[i] // <-- line 1111 >>>>>> >>>>>> (*URL).Equal >>>>>> <https://gitlab.com/accumulatenetwork/accumulate/-/blob/5b1cb612d76d4163a101303e51a6fd352224cdab/pkg/url/url.go#L465> >>>>>> : >>>>>> >>>>>> func (u *URL) Equal(v *URL) bool { >>>>>> if u == v { >>>>>> return true >>>>>> } >>>>>> if u == nil || v == nil { >>>>>> return false >>>>>> } >>>>>> return strings.EqualFold(u.String(), v.String()) >>>>>> } >>>>>> >>>>>> (*URL).String >>>>>> <https://gitlab.com/accumulatenetwork/accumulate/-/blob/5b1cb612d76d4163a101303e51a6fd352224cdab/pkg/url/url.go#L240> >>>>>> : >>>>>> >>>>>> func (u *URL) String() string { >>>>>> if u.memoize.str != "" { >>>>>> return u.memoize.str >>>>>> } >>>>>> >>>>>> u.memoize.str = u.format(nil, true) >>>>>> return u.memoize.str >>>>>> } >>>>>> >>>>>> (*URL).format >>>>>> <https://gitlab.com/accumulatenetwork/accumulate/-/blob/5b1cb612d76d4163a101303e51a6fd352224cdab/pkg/url/url.go#L189> >>>>>> : >>>>>> >>>>>> func (u *URL) format(txid []byte, encode bool) string { >>>>>> var buf strings.Builder >>>>>> // ... write to the builder >>>>>> return buf.String() >>>>>> } >>>>>> >>>>>> How is this possible? Based on `addr=0x0` in the panic I think this >>>>>> is a nil pointer panic, as opposed to some other kind of segfault. The >>>>>> only >>>>>> way I can reproduce panic-on-string-index is with >>>>>> `(*reflect.StringHeader)(unsafe.Pointer(&s)).Data >>>>>> = 0`, but I don't see how that can be happening here. I'm saving the >>>>>> string >>>>>> but I'm not doing anything weird with it. And the string header is a >>>>>> value >>>>>> type so code that manipulates the returned string shouldn't modify the >>>>>> original. And I'm definitely not doing any kind of unsafe string >>>>>> manipulation like that in my code, anywhere. The only reference to unsafe >>>>>> anywhere in my code is for parameters for calling GetDiskFreeSpaceExW >>>>>> (Windows kernel32.dll call). >>>>>> >>>>>> -- >>>>>> 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...@googlegroups.com. >>>>>> To view this discussion on the web visit >>>>>> https://groups.google.com/d/msgid/golang-nuts/d6f6bb75-45e9-4a38-9bbd-d332e7f3e57cn%40googlegroups.com >>>>>> <https://groups.google.com/d/msgid/golang-nuts/d6f6bb75-45e9-4a38-9bbd-d332e7f3e57cn%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>> . >>>>>> >>>>> -- >>>> 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...@googlegroups.com. >>>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/golang-nuts/31f77ff2-cf11-4b3e-9b14-874b6cc41da3n%40googlegroups.com >>>> <https://groups.google.com/d/msgid/golang-nuts/31f77ff2-cf11-4b3e-9b14-874b6cc41da3n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> -- >>> >> You received this message because you are subscribed to a topic in the >>> Google Groups "golang-nuts" group. >>> To unsubscribe from this topic, visit >>> https://groups.google.com/d/topic/golang-nuts/Dgy0fyb4Shw/unsubscribe. >>> To unsubscribe from this group and all its topics, send an email to >>> golang-nuts...@googlegroups.com. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/golang-nuts/CAEkBMfG8v0qO_NP4PipEBL%3Dd_Ase9ntWi4EL1dQE_6ubeZQnww%40mail.gmail.com >>> <https://groups.google.com/d/msgid/golang-nuts/CAEkBMfG8v0qO_NP4PipEBL%3Dd_Ase9ntWi4EL1dQE_6ubeZQnww%40mail.gmail.com?utm_medium=email&utm_source=footer> >>> . >>> >> -- > 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/65400cce-b60c-4bb0-97a7-a963c6621098n%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/65400cce-b60c-4bb0-97a7-a963c6621098n%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- 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/CAEkBMfHQa0JrYoM5HHwVYEfU7dNGSrfyDfp1%2BpoN_0K_Cqr5ew%40mail.gmail.com.