Last active
January 12, 2024 15:52
-
-
Save Integralist/fad304fd5f435b8f400d6adad693a8a2 to your computer and use it in GitHub Desktop.
[Go pointer receiver method avoids runtime panic] #go #golang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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