10.8 Struct Memory Layout and Padding

Right, let’s talk about what your computer actually does when you define a struct. It’s not just neatly stacking your fields in a row like a perfectly organized bookshelf. It’s more like a Tetris game played by a slightly obsessive-compulsive robot whose only goal is to make the CPU’s life easier, even if it wastes a bit of memory in the process. This is the world of memory alignment and padding, and if you ignore it, you can accidentally write code that’s hilariously inefficient.

10.7 Comparing Structs: When == Works and When It Doesn't

Right, so you want to compare two structs. Your first instinct, the good ol’ == operator, is a solid one. It works perfectly… until it doesn’t. And when it doesn’t, it fails with a spectacularly unhelpful compiler error that essentially tells you, “You can’t compare these, and I’m not going to tell you why.” Let’s demystify that. The golden rule is simple: you can use == and != on structs only if all their fields are themselves comparable. A field is comparable if you can use == on it. Think basic types: string, int, bool, etc., or arrays of those types, or other structs made entirely of comparable types. It’s comparability turtles all the way down.

10.6 Struct Tags: encoding/json, db, and Custom Tags

Now, let’s talk about struct tags, the little backtick-enclosed strings of metadata that make Go’s reflection magic actually useful. You’ve probably seen them hovering next to your struct fields like json:"name". They look like comments, but they are absolutely not. They’re a key part of your type’s definition, and the reflect package can read them. This is how we tell various encoders, ORMs, and other sorcerers how to handle our data.

10.5 Promoted Methods and Name Collision

Now, let’s talk about what happens when you start embedding structs willy-nilly and the language starts promoting methods. It’s a fantastic feature until it isn’t. Go’s method promotion is like a well-meaning but overzealous intern: it tries to be helpful by making embedded fields’ methods available on the parent struct, but it has absolutely no sense of subtlety or conflict resolution. When you embed a type, any methods defined on that type get “promoted” to the enclosing struct. This means you can call myParentStruct.TheEmbeddedStructsMethod() directly. It’s syntactic sugar, but it’s the good kind that makes your coffee taste better.

10.4 Struct Embedding: Promoting Fields and Methods

Right, so you’ve got your structs defined. You’ve got your User with a Name and an ID. Neat. But now you’re probably thinking, “I’ve got this AdminUser that’s like a User but with, you know, admin powers.” Your first instinct might be to reach for inheritance. Stop it. This isn’t that kind of party. Go offers a different, and frankly more composable, approach: embedding. Think of it as structural delegation, not inheritance. You’re embedding one struct inside another, and the fields and methods of the inner struct get promoted to the outer struct. It’s like the outer struct suddenly gets all the abilities of the inner one without you having to write a bunch of tedious pass-through methods.

10.3 Anonymous Struct Types

Right, anonymous structs. You’ve probably seen these little weirdos out in the wild and maybe scratched your head. They look like a struct got lost and forgot to declare itself. And you’d be right. An anonymous struct is exactly that: a struct type defined without a name, usually declared and used in the same place. It’s the ultimate “use it and lose it” data structure. We use them for two main reasons: when we need a one-off, highly localized data container, or when we’re dealing with something like JSON unmarshaling and we want to pluck a few specific fields out of a giant blob without defining a whole new named type. They’re convenient, but like most convenient things (see: fast food, duct tape), they come with caveats.

10.2 Struct Literals: Positional and Named Field Forms

Right, let’s talk about giving your structs actual life. You’ve defined a beautiful blueprint with type MyStruct struct {...}, but a blueprint isn’t a house. To get an actual instance—a real, living, breathing chunk of data in memory—you need a struct literal. And Go, in its frustratingly pragmatic way, gives you two main flavors to choose from: positional and named. One is terse and dangerous, the other is verbose and safe. You can probably guess which one I use 99% of the time.

10.1 Defining Structs and Instantiating Them

Let’s get one thing straight: you’re not dealing with Java classes here. A Go struct is a beautifully simple, brutally efficient collection of named fields. It’s a way to say, “These pieces of data belong together,” without the ceremony of a full-blown object-oriented system. You define one with the type and struct keywords. It looks like this: type User struct { ID int Username string Email string IsActive bool LastLogin time.Time } Congratulations, you’ve just created a new type, User. It’s now a first-class citizen in your program, just like int or string. You can use it in function signatures, as a slice element, or as a map value. This is the first big win: creating a vocabulary for your domain.

— joke —

...