Skip to content

Instantly share code, notes, and snippets.

@Integralist
Last active January 12, 2024 15:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Integralist/fad304fd5f435b8f400d6adad693a8a2 to your computer and use it in GitHub Desktop.
Save Integralist/fad304fd5f435b8f400d6adad693a8a2 to your computer and use it in GitHub Desktop.
[Go pointer receiver method avoids runtime panic] #go #golang
// https://play.golang.com/p/NOxcQO8U-G7
//
// IMPORTANT: Even though you can call the pointer receiver method, you still can't access a field without triggering a runtime panic. But just being able to call the method is still useful/interesting.
//
// The explanation for this is as follows...
//
// There’s two things going on here. The first is calling a method. Go knows which method to call thanks to the name of the method and the type of the receiver. That’s all there is to knowing which method to call.
// e.g. The Go runtime evaluates the code `e.PointerMethod()` into something like `(*Example).PointerMethod(e)`.
// The trick to understand the rest is to remember that having a pointer be nil is not a reason to panic just yet. You only panic when you try to do something that needs to dereference that nil pointer.
// Now when the method gets called, the sugared version (e.g. `(*Example).PointerMethod(e)`) shows that the first argument is the receiver.
// If that receiver is a pointer, then Go gives that even if it’s nil and that’s fine since it doesn’t get dereferenced (until you dereference it yourself and that panics).
// If you try to pass a nil pointer by value, Go will need to dereference it and will panic before having a chance to call your method.
// This is why calling a method with a value receiver will trigger a runtime panic.
package main
import (
"fmt"
)
type Example struct {
Field int
}
func (e *Example) PointerMethod() {
fmt.Printf("PointerMethod: %#v (%T)\n", e, e)
fmt.Printf("PointerMethod field access: %#v (%T)\n", e.Field, e.Field) // this is a runtime panic
}
func (e Example) ValueMethod() {
fmt.Printf("ValueMethod: %#v (%T)\n", e, e)
}
func NewExample() *Example {
return nil
}
func main() {
e := NewExample()
fmt.Printf("main: %#v (%T)\n", e, e) // (*main.Example)(nil) (*main.Example)
e.PointerMethod() // this is ok! :mindblown:
e.ValueMethod() // this is a runtime panic
fmt.Println(e.Field) // this is a runtime panic
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment