11.7 Choosing Between Value and Pointer Receivers: The Rules

Alright, let’s cut through the noise. Choosing between value and pointer receivers isn’t about memorizing a list of rules; it’s about understanding what you’re telling the compiler to do. Get this right, and your code is efficient and predictable. Get it wrong, and you’ll have a delightful time chasing bugs that make no sense. My favorite. The core principle is embarrassingly simple: do you need to modify the receiver’s state? If yes, use a pointer (*T). If no, you probably want a value (T). But of course, it’s never that simple, is it? We have to talk about efficiency, method sets, and the dreaded implicit indirection.

11.6 Method Values and Method Expressions

Right, let’s talk about something that seems like it should be simple but trips up a lot of smart people: getting a handle on a method itself, not just calling it on a specific variable. We’re talking about method values and method expressions. The difference is subtle in name but massive in practice. It all comes down to one question: is the receiver already baked in, or do you have to supply it later?

11.5 Methods on Non-Struct Types

Alright, let’s talk about something that makes a lot of new Gophers do a double-take: putting methods on types that aren’t structs. You’ve probably plastered methods all over your User and Account structs. That’s great. But what about when you want to add behavior to, say, a string? Or a slice? Or that custom type you made for a float64? You absolutely can. In Go, you can define a method on any type you define in your package, provided the type’s underlying definition (its “type literal”) is in the same package. This is the secret handshake. You can’t add a method to a built-in type like string or int directly because you didn’t define them—they belong to the builtin package. But you can create a new type with that as its underlying type, and then that new type is yours. You can do whatever you want to it. Even give it methods.

11.4 Calling Methods on nil Pointers

Right, so you’ve got a pointer receiver. You’ve got a variable that’s nil. You call a method on it. Your gut says this should be a one-way ticket to panicville, right? Well, put that gut on hold, because Go is about to show you one of its more interesting, and frankly, brilliantly pragmatic, party tricks. In most languages, calling a method on a nil reference is the runtime equivalent of jumping off a cliff while yelling “I regret nothing!” It’s an immediate segfault or a null pointer exception. Go, however, in its relentless pursuit of being useful rather than pedantic, says, “Hold my beer.” It is perfectly valid to call a method on a nil pointer receiver. The method will execute. Now, whether that’s a good idea or not depends entirely on what you wrote inside that method.

11.3 Method Sets: Which Methods Are in Scope for a Type

Right, let’s talk about method sets. This is where the theoretical “methods are just functions with receivers” meets the practical, rubber-meets-the-road reality of the Go compiler deciding whether you can even call that method. It’s the rulebook, and if you don’t know the rules, you’ll be left shouting at the referee. The core concept is deceptively simple: a method set is the list of methods attached to a given type. But the scope of that list—which methods are available for you to call in a given context—depends on whether you’re dealing with the type itself (T) or a pointer to the type (*T). And this is where most of the confusion, and frankly, the compiler errors, come from.

11.2 Value Receivers vs Pointer Receivers: Mutation and Copying

Alright, let’s get into the weeds on this one. The choice between value and pointer receivers isn’t just academic; it dictates whether your code will be efficient, correct, or a complete head-scratcher when it fails. At its core, this whole debate boils down to one question: are you trying to change the state of the thing you’re calling the method on, or are you just using its current state? Think of a value receiver like getting a photocopy of a document. You can scribble all over your copy, highlight things, tear it up—the original remains pristine. A pointer receiver is like someone handing you the original document and a pen. Your changes are permanent and everyone else sees them.

11.1 Defining Methods: func (r Receiver) Name()

Alright, let’s get our hands dirty with methods. Forget the dry theory for a second. At its core, a method in Go is just a function with a special guest star in its parameter list: the receiver. It’s how we attach functionality to our types. The syntax is the first thing that trips people up, so let’s break it down. You define a method like this: func (r Receiver) Name(parameters) returnType { // your brilliant code here } That (r Receiver) bit before the function name is the receiver. It’s the anchor that ties this function to a specific type. When you call myObject.Name(), it’s essentially passing myObject as that r parameter under the hood. It’s syntactic sugar, but it’s the good kind that makes your code readable and organized.

— joke —

...