On Wed, 9 Oct 2024 at 09:54, Ken Lee <ken.lee.kiany...@gmail.com> wrote:
> So can I say that, if I'm not writing concurrency code, it's acceptable > for me to mix pointer and value receiver? > Note that you can't really know, if your users use concurrency or not. And I still maintain, that concurrency is - mostly - a red herring. Yes, calling a method with value receiver is a copy and hence a read, so it is a data race when done concurrently with a write. But there are many possible ways to read or write to a variable. I still stand by the point that there is no epidemic of data races on time.Time.UnmarshalJSON calls, in practice. So obviously, there *are* ways to mix receiver kinds that does not practically incur this risk of races. I also think, it is a good example to look at how that works: time.Time is *in general* used and passed as a value, so over its normal life, it is never concurrently read/written too - it's mostly read-only. Only in limited circumstances, related to its initial population is it really written to - and those are the calls with pointer receivers. And the same is true for the Enum example I linked: It implements flag.Value and essentially, it is accessed via pointer during flag parsing, but after that used as a value. So make the `Set` method a pointer receiver and anything else a value receiver. It's a more fuzzy lesson, but I think it's more accurate than talking about concurrency. > I find that like what @Axel said, I think there's some struct in STD Lib > doing this too? case in point, Time struct in "time" package. > > On Tuesday 8 October 2024 at 10:29:09 pm UTC+8 Robert Engels wrote: > >> And when I provided data and reasoning from “the other side of the table” >> including code samples, you responded with “Meanwhile, you seem to be >> aggressively ignoring what I actually wrote. I find that pretty rude.” >> >> You need to learn the concept of “in addition to” or “agree, but this is >> more important” and lose your penchant to immediately rudely escalate the >> discussion with direct or veiled name calling. >> >> On Oct 8, 2024, at 1:37 AM, 'Axel Wagner' via golang-nuts < >> golan...@googlegroups.com> wrote: >> >> >> >> Just to clarify: From what I can tell, I am (in this revival of the >> thread) the only one on the "mixing receiver kinds is sometimes necessary, >> so we shouldn't caution against it" side of the table and indeed opened it. >> As such, I'm already on the back foot. I tried to at least acknowledge the >> arguments from the other side, even if I don't have much to say about them. >> I don't believe it's rude to ask for the same from the other side of the >> table. >> >> I don't expect anyone to be invested in convincing me, as a person. But >> (again, from what I can tell) I represent a side of the discussion here. >> And it's not possible to have a discussion where one side is simply ignored. >> >> On Tue, 8 Oct 2024 at 07:02, Robert Engels <ren...@ix.netcom.com> wrote: >> >>> And if you don’t recognize the ass clown rudeness in a statement like “ >>> No offence, but I made an argument. You don't have to agree with the >>> argument and it might be wrong. But to convince me, at least, that argument >>> would need to actually be referenced.” you are a narcissistic ahole. >>> >>> On Oct 7, 2024, at 11:20 PM, Axel Wagner <axel.wa...@googlemail.com> >>> wrote: >>> >>> >>> You are trying to prove something nobody actually doubted. Meanwhile, >>> you seem to be aggressively ignoring what I actually wrote. I find that >>> pretty rude. >>> >>> On Tue, 8 Oct 2024 at 01:15, robert engels <ren...@ix.netcom.com> wrote: >>> >>>> Here is a slightly easier version to see the race between the mutation >>>> and the copy for the value method: >>>> >>>> package main >>>> >>>> import ( >>>> "log" >>>> "sync" >>>> ) >>>> >>>> type S struct { >>>> lock *sync.Mutex >>>> index int >>>> values [128]int >>>> } >>>> >>>> func (s *S) mutate() { >>>> s.lock.Lock(); >>>> defer s.lock.Unlock(); >>>> s.index++; >>>> for i:=0; i< 128; i++ { >>>> s.values[i]=s.index; >>>> } >>>> } >>>> >>>> func (s S) validate() { >>>> for i:=0;i<128;i++ { >>>> if s.values[i]!=s.index { >>>> log.Fatal("mismatch error") >>>> } >>>> } >>>> } >>>> >>>> func doit(s *S) { >>>> for { >>>> s.mutate() >>>> s.validate() >>>> } >>>> } >>>> >>>> func main() { >>>> var s S >>>> var lock sync.Mutex >>>> s.lock = &lock >>>> var wg sync.WaitGroup >>>> wg.Add(1) >>>> for i:=0;i<64;i++ { >>>> go doit(&s) >>>> } >>>> wg.Wait() >>>> } >>>> >>>> >>>> On Oct 7, 2024, at 6:06 PM, robert engels <ren...@ix.netcom.com> wrote: >>>> >>>> I wrote a simple test. Sure enough it fails, and it reports a data race. >>>> >>>> package main >>>> >>>> import ( >>>> "log" >>>> "sync" >>>> ) >>>> >>>> type S struct { >>>> sync.Mutex >>>> index int >>>> values [128]int >>>> } >>>> >>>> func (s *S) mutate() { >>>> s.Lock(); >>>> defer s.Unlock(); >>>> s.index++; >>>> for i:=0; i< 128; i++ { >>>> s.values[i]=s.index; >>>> } >>>> } >>>> >>>> func (s S) validate() { >>>> for i:=0;i<128;i++ { >>>> if s.values[i]!=s.index { >>>> log.Fatal("mismatch error") >>>> } >>>> } >>>> } >>>> >>>> func doit(s *S) { >>>> for { >>>> s.mutate() >>>> s.validate() >>>> } >>>> } >>>> >>>> func main() { >>>> var s S >>>> var wg sync.WaitGroup >>>> wg.Add(1) >>>> for i:=0;i<64;i++ { >>>> go doit(&s) >>>> } >>>> wg.Wait() >>>> } >>>> >>>> In fact, you get a linter warning, because of the copy of the mutex in >>>> calling the value method - since it knows it should be a reference. >>>> >>>> >>>> On Oct 7, 2024, at 5:30 PM, Robert Engels <ren...@ix.netcom.com> wrote: >>>> >>>> I am fairly certain if you mix pointer and receiver methods and the >>>> receiver methods mutate - even if you synchronize those you will get a data >>>> race calling the value methods. It must afaik as the runtime/compiler has >>>> no implicit synchronization when creating the copies. That is a data race. >>>> >>>> On Oct 7, 2024, at 5:10 PM, Axel Wagner <axel.wa...@googlemail.com> >>>> wrote: >>>> >>>> >>>> My argument had nothing to do with synchronization. >>>> >>>> FTR I find the synchronization argument also extremely dubious. By that >>>> argument, you also can't pass the address to a local variable to another >>>> function, when using it as a value elsewhere. It's a weird argument to >>>> make. time.Time uses a mix of pointer- and value receivers and IMO no one >>>> can make a serious argument that this would expose programs to risks of >>>> data races. >>>> >>>> But to repeat my actual argument in favour of (sometimes) mixing >>>> receiver kinds: >>>> 1. It is totally reasonable to use some types as values. >>>> 2. Such types, intended to be used as values, will need to use >>>> value-receivers for some methods, as otherwise their value-version does not >>>> implement certain interfaces (methods are not promoted from pointer to >>>> value types). Like fmt.Stringer, for example. And >>>> 3. such types still need to sometimes use pointer-receivers, to >>>> implement functionalities like unmarshalling. >>>> >>>> time.Time is a standard library example of such a type. I also provided >>>> an example for an "enum-like" type implementing flag.Value. >>>> >>>> On Mon, 7 Oct 2024 at 23:57, Robert Engels <ren...@ix.netcom.com> >>>> wrote: >>>> >>>>> I am pretty sure it is immaterial. If the object isn’t immutable any >>>>> copy or mutation operation needs to be synchronized. >>>>> >>>>> But the problem afaik is that you can’t control synchronization when >>>>> the object is copied for a value receiver - which means you cant properly >>>>> synchronize when you have pointer and value receivers unless you do it >>>>> externally (which is a huge pain to do everywhere). >>>>> >>>>> On Oct 7, 2024, at 4:43 PM, 'Axel Wagner' via golang-nuts < >>>>> golan...@googlegroups.com> wrote: >>>>> >>>>> >>>>> No offence, but I made an argument. You don't have to agree with the >>>>> argument and it might be wrong. But to convince me, at least, that >>>>> argument >>>>> would need to actually be referenced. >>>>> >>>>> I gave reasons why, in my opinion, *not* mixing value and pointer >>>>> receivers sometimes leads to incorrect code. So as far as I'm concerned >>>>> (until someone tells me my reasons are wrong) Goland's linter simply >>>>> encourages you to write bad code. It would not be the first time that I >>>>> strongly disagree with the recommendations of an IDE. Goland in particular >>>>> has a history of making, in my opinion, pretty questionable decisions. >>>>> >>>>> On Mon, 7 Oct 2024 at 22:39, Cleberson Pedreira Pauluci < >>>>> pauluci....@gmail.com> wrote: >>>>> >>>>>> Many places and books I've read generally say: If a function needs to >>>>>> update a variable, or if an argument is so large that we want to avoid >>>>>> copying it, we should pass the pointer. Same for methods (pointer >>>>>> receiver). (The Go programming language book). >>>>>> >>>>>> About mixing "value receiver" and "pointer receiver". Even the IDE >>>>>> complains about this and recommends following the Go documentation. >>>>>> (Goland) >>>>>> >>>>>> >>>>>> Em segunda-feira, 7 de outubro de 2024 às 15:15:25 UTC-3, burak >>>>>> serdar escreveu: >>>>>> >>>>>>> Mixing pointer and value receivers can be race-prone, because of the >>>>>>> copying involved in passing value receivers. >>>>>>> >>>>>>> On Mon, Oct 7, 2024 at 12:03 PM 'Axel Wagner' via golang-nuts >>>>>>> <golan...@googlegroups.com> wrote: >>>>>>> > >>>>>>> > To be honest, I always found this recommendation a little bit >>>>>>> strange, personally. >>>>>>> > >>>>>>> > I'll note that the standard library does not really keep to this >>>>>>> either. For example, time.Time.UnmarshalText (obviously) has a >>>>>>> pointer-receiver, while almost all other methods on time.Time have a >>>>>>> value >>>>>>> receiver. >>>>>>> > And if you implement flag.Value, the Set method obviously needs a >>>>>>> pointer receiver, but if the String method has one as well, it won't >>>>>>> print >>>>>>> properly when used as a value. In basically every implementation of >>>>>>> flag.Value I've ever written, String needed a value receiver, while Set >>>>>>> needed a pointer receiver. >>>>>>> > >>>>>>> > I understand the basic idea of the advice, that if a type keeps >>>>>>> state that is manipulated via methods, then it should generally be >>>>>>> passed >>>>>>> around as a pointer, so giving all the methods a pointer-receiver works >>>>>>> well. But if a type *is* intended to be used as a value (like time.Time >>>>>>> or >>>>>>> Enum in my example) then you will almost certainly end up with a mix of >>>>>>> receiver kinds - as soon as you want to add any form of >>>>>>> de-serialization to >>>>>>> it. So "don't mix receiver kinds" seems like misleading advice to me. >>>>>>> > >>>>>>> > On Mon, 7 Oct 2024 at 19:44, Ian Lance Taylor <ia...@golang.org> >>>>>>> wrote: >>>>>>> >> >>>>>>> >> On Mon, Oct 7, 2024 at 10:29 AM Ken Lee <ken.lee....@gmail.com> >>>>>>> wrote: >>>>>>> >> > >>>>>>> >> > --- >>>>>>> >> > There is a consideration to make, though: historically it has >>>>>>> been considered bad form in Go to give a type a mix of value and pointer >>>>>>> receivers in methods without a very specific reason for doing so. >>>>>>> >> > --- >>>>>>> >> > >>>>>>> >> > Is this still the case now? As in 2024. >>>>>>> >> >>>>>>> >> As a general guideline, yes. >>>>>>> >> >>>>>>> >> https://go.dev/wiki/CodeReviewComments#receiver-type >>>>>>> >> >>>>>>> >> Ian >>>>>>> >> >>>>>>> >> >>>>>>> >> >>>>>>> >> > On Sunday 13 January 2013 at 7:03:29 am UTC+8 Kevin Gillette >>>>>>> wrote: >>>>>>> >> >> >>>>>>> >> >> Indeed. In addition to implicit dereferencing for value >>>>>>> receivers, the reverse also works as well: anything that is addressable >>>>>>> (including 'value' variables on the stack, or a field of element of >>>>>>> anything that's addressable) will implicitly be addressed when a >>>>>>> pointer-receiver method is called on them (though you must explicitly >>>>>>> use >>>>>>> the address operator when you need to pass value variables as pointers). >>>>>>> >> >> >>>>>>> >> >> There is a consideration to make, though: historically it has >>>>>>> been considered bad form in Go to give a type a mix of value and pointer >>>>>>> receivers in methods without a very specific reason for doing so. The >>>>>>> typical justification is that a small struct in a getter method might as >>>>>>> well have a value receiver even though the corresponding setter method >>>>>>> uses >>>>>>> a pointer receiver; this, however, can lead to confusion on the part of >>>>>>> the >>>>>>> app programmer if they start out using only the read-only methods upon >>>>>>> what >>>>>>> turns out to be a value-copy of the original (but hey, it compiled and >>>>>>> seems to work, so it must be correct) -- when use of pointer-receiver >>>>>>> methods don't seem to produce the documented changes in the original, it >>>>>>> can be difficult to debug. >>>>>>> >> >> >>>>>>> >> >> >>>>>>> >> >> On Saturday, January 12, 2013 3:17:16 PM UTC-7, Dave Collins >>>>>>> wrote: >>>>>>> >> >>> >>>>>>> >> >>> On Saturday, January 12, 2013 3:52:35 PM UTC-6, Taric Mirza >>>>>>> wrote: >>>>>>> >> >>>> >>>>>>> >> >>>> Thanks! Works like a charm and is helping cleaning up my >>>>>>> code a ton. >>>>>>> >> >>>> >>>>>>> >> >>>> One other question, this is really more about coding style: >>>>>>> >> >>>> >>>>>>> >> >>>> In the case where you manipulate members of the struct, then >>>>>>> using >>>>>>> >> >>>> pointers as in your example is the way to go. >>>>>>> >> >>>> >>>>>>> >> >>>> But, you have a choice for functions that just read values >>>>>>> from the >>>>>>> >> >>>> struct instead of manipulating it. Is there a best practice >>>>>>> coding >>>>>>> >> >>>> style here, between dereferencing the struct and then using >>>>>>> that, or >>>>>>> >> >>>> dereferencing each member of the struct as you go? eg: >>>>>>> >> >>>> >>>>>>> >> >>>> // A: >>>>>>> >> >>>> >>>>>>> >> >>>> laser := worldobj.(*Laser) >>>>>>> >> >>>> fmt.Printf("%0.4f,%0.4f", (*laser).x, (*laser).y) >>>>>>> >> >>>> >>>>>>> >> >>>> versus >>>>>>> >> >>>> >>>>>>> >> >>>> // B: >>>>>>> >> >>>> >>>>>>> >> >>>> laser := *(worldobj.(*Laser)) >>>>>>> >> >>>> fmt.Printf("%0.4f,%0.4f", laser.x, laser.y) >>>>>>> >> >>>> >>>>>>> >> >>>> >>>>>>> >> >>>> I'm kind of torn. I would imagine A) has slightly better >>>>>>> >> >>>> performance, and doesn't require any code-rework if you >>>>>>> later on need >>>>>>> >> >>>> to manipulate the struct. >>>>>>> >> >>>> >>>>>>> >> >>>> On the other hand, B) is more readable since you don't have >>>>>>> to look at >>>>>>> >> >>>> pointers all over the place, just on one line. >>>>>>> >> >>> >>>>>>> >> >>> >>>>>>> >> >>> Actually, you don't need to dereference at all. Go >>>>>>> automatically handles this for you. >>>>>>> >> >>> >>>>>>> >> >>> See this example: http://play.golang.org/p/ANaKaFSQLn >>>>>>> >> >>> >>>>>>> >> > -- >>>>>>> >> > 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/03df7dce-5c48-44a3-bc3c-851ded2a1f08n%40googlegroups.com. >>>>>>> >>>>>>> >> >>>>>>> >> -- >>>>>>> >> 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/CAOyqgcX7v9Edk5beRH38tfJO18ZUXv-nOHsEPPCfMQy0hz%3DFdw%40mail.gmail.com. >>>>>>> >>>>>>> > >>>>>>> > -- >>>>>>> > 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/CAEkBMfGcq2nxaik_qAWoX81W-tTKRRYBDM5_6%3DefSv4tr8b03g%40mail.gmail.com. >>>>>>> >>>>>>> >>>>>> >>>>>> -- >>>>>> 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/9b28006b-c310-417e-9afc-e7f5c470641cn%40googlegroups.com >>>>>> <https://groups.google.com/d/msgid/golang-nuts/9b28006b-c310-417e-9afc-e7f5c470641cn%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/CAEkBMfEj%3DQACB31VMc7ami7xt9tMF00kYxFUfZpWfZ0j65GWsw%40mail.gmail.com >>>>> <https://groups.google.com/d/msgid/golang-nuts/CAEkBMfEj%3DQACB31VMc7ami7xt9tMF00kYxFUfZpWfZ0j65GWsw%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...@googlegroups.com. >>>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/golang-nuts/CAEkBMfFYZ1DTD9fTVzNHtOp7Ed7w3_x8QbxsB2x_%2BTs%3DtxY0BA%40mail.gmail.com >>>> <https://groups.google.com/d/msgid/golang-nuts/CAEkBMfFYZ1DTD9fTVzNHtOp7Ed7w3_x8QbxsB2x_%2BTs%3DtxY0BA%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...@googlegroups.com. >>>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/golang-nuts/B6F948A5-9F2E-4698-85D1-17B862779901%40ix.netcom.com >>>> <https://groups.google.com/d/msgid/golang-nuts/B6F948A5-9F2E-4698-85D1-17B862779901%40ix.netcom.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/_MEf-I49OTo/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/CAEkBMfGb7xEu%3Da73xWUBuAGK2T3_R7uA4K5FZYr4vYzkLpTxqg%40mail.gmail.com >> <https://groups.google.com/d/msgid/golang-nuts/CAEkBMfGb7xEu%3Da73xWUBuAGK2T3_R7uA4K5FZYr4vYzkLpTxqg%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/0f008c03-200a-4fa5-8198-3b1f0a227f9cn%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/0f008c03-200a-4fa5-8198-3b1f0a227f9cn%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/CAEkBMfE5gDYrCDrM4T%3DFh%3DoE1d7xFkP3SNXQdPyLZCfYR_2c3Q%40mail.gmail.com.