Oh and regarding your question about checking for nil: My recommendation is
to just not do that. Given that the method has a value receiver, it is
clear that calling it on a nil-pointer is a bug. A panic, with traceback,
is the most useful thing you can do in that situation anyways, as it allows
you to find the bug more quickly.

If you try to guard at every point against someone introducing a bug, the
possible state-space of your program will explode. It will become
impossible to reason about, as there are no invariants to rely on and
everything gets riddled with extra checks and assertions, introducing hard
to follow extra control flow. In my experience, that will end up
introducing more bugs into your code than your checks prevent. Simpler,
more obvious code is easier to test, review and check for problems. And has
fewer bugs in practice.

On Tue, 10 Jun 2025 at 23:12, Axel Wagner <axel.wagner...@googlemail.com>
wrote:

> Hi,
>
> assume that method values were not promoted to pointer types.
> And pointer receiver methods can obviously not get promoted to value
> types: If you pass
> var v T; f(v)
> you don't expect `v` to be modified. But pointer methods would expect
> that. You'd get hard to find, confusing bugs.
> So the method set of a type is exactly the set of methods declared with
> that receiver type.
>
> Now consider flag.Value <https://pkg.go.dev/flag#Value>.
> Obviously, the Set method requires a pointer receiver *T.
> If String would have a value receiver T, then neither T nor *T would
> implement flag.Value: Neither type has *both* methods.
> So String also needs to have a pointer receiver.
> But that means, if you do e.g.
> var t time.Time; fmt.Println(t)
> the String method would not get called - it would format as something like
> {42,23,0xdeadbeef}.
>
> The promotion of value receiver methods to pointer types means it is
> possible for a type to mix receiver kinds. Methods that make more sense on
> a value - like String - can use a value receiver, while methods that
> require a pointer - like Set - can still use a pointer receiver.
> Yes, this means the program will panic if you call String on a
> nil-pointer. But… well, don't do that, then. It would panic if the receiver
> was a pointer receiver as well, as soon as it tries to access on of the
> receivers fields (which it needs to do to stringify).
>
> What is a little bit confusing is, that while pointer receiver methods are
> not promoted to value types, you can still do
> var b strings.Builder; b.WriteString("foo")
> despite WriteString having a pointer receiver. That's because the selector
> expression becomes syntactic sugar for (&b).WriteString under these
> circumstances.
> Personally, I kind of feel this was a mistake, but c'est la vie.
>
> On Tue, 10 Jun 2025 at 22:06, Alexander Shopov <a...@kambanaria.org> wrote:
>
>> Does anyone have any idea why method sets were designed like this?
>>
>> Method sets of pointer types include the methods defined on the type
>> that the pointer points to (Explicitly pointed out in spec
>> https://go.dev/ref/spec#Method_sets)
>>
>> I get the idea that having a pointer means that one can mutate the
>> value - so in essence it may be useful to have the pointer method set
>> contain the value method set.
>> But having a pointer to a value does not guarantee the existence of value.
>>
>> Let's have a look at this trivial example:
>> https://go.dev/play/p/ztNRPYfoAuk
>>
>> type num interface {
>>
>> num() int
>> }
>>
>> type example struct{}
>>
>> func (e example) num() int {
>>   return 42
>> }
>>
>> func useNum(n num) {
>>   fmt.Println(n == nil)
>>   v := reflect.ValueOf(n)
>>   fmt.Println(v.Kind() == reflect.Ptr && v.IsNil())
>>   fmt.Println(n.num())
>> }
>>
>> func main() {
>>   var e1 example
>>   var e2 *example
>>   useNum(e1)
>>   useNum(e2)
>> }
>>
>> useNum has no safe way to call the num() method, even though we are
>> sure it is safe to call on a nil, in essence it never accesses the
>> state of the example struct.
>> However a caller can substitute a value with a pointer. Within the
>> useNum function we cannot guard with a n != nil since it receives an
>> interface and it will not be nil.
>> The only way to guard is with reflection.
>>
>> This same method is also used in Go standard library:
>> - https://go.dev/src/fmt/print.go#L592
>> - https://go.dev/src/log/slog/handler.go#L564
>>
>> So given this - is there any source on why method sets were designed this
>> way?
>>
>> Kind regards:
>> al_shopov
>>
>> --
>> 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 visit
>> https://groups.google.com/d/msgid/golang-nuts/CAP6f5Mm1eF4XZw5sJMvrP4-sb2DkppoFuAtzbwUcbdXQyP1J8w%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+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/golang-nuts/CAEkBMfHkXvmhZKve1GN84e1CX6UMU7kqNGnfx0HsaMg%2BUKmQ4w%40mail.gmail.com.

Reply via email to