I agree with what Axel said in the below: ```a lot of people *do* take it as a pretty hard rule. Not as hard as a compilation error or a vet failure, but still in a way that they think it is generally agreed that it should never be done. ```
The way I read the https://go.dev/wiki/CodeReviewComments#receiver-type sounds more like a hard rule rather than one with exceptions. Especially when it's a guide for beginners to Golang, how would they know when are the exceptions when they are new? They might take this as a hard rule and brings up in PR reviews, it would be hard to change one's already established opinion. On Wednesday 9 October 2024 at 5:56:03 pm UTC+8 Axel Wagner wrote: On Wed, 9 Oct 2024 at 09:54, Ken Lee <ken.lee....@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...@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/cc3864e1-0847-4924-accb-cbac93b4cf42n%40googlegroups.com.