This content originally appeared on Level Up Coding - Medium and was authored by Jacob Kim
When I was first learning how to use Go, the idea of Go being a non-OOP language was interesting. I was used to using Python, which was an OOP language, and thus had a class. I thought it was an odd design choice because I couldn't really imagine a world where I couldn't use a class. But after extensive programming in Go, I really enjoyed the Go way of writing code. Go does not have a class, but has structs. Structs can have methods attached to them, just like classes.
What are structs?
Structs are complex data types that hold other types. There are some things that we would like to represent as a group of attributes. For example, say that we have a laptop. A laptop isn't just one thing. Rather, it's a combination of many parts. So we could represent a laptop as a struct, like so.
type laptop struct {
cpu string
ram int
storage int
manufacturer string
}
Let's say that we want to represent a MacBook Air. We can do this:
mba := laptop{"M2", 16, 256, "Apple"}
This is cool because we can group multiple types into logically coherent objects.
How are structs different from classes?
People who have used another programming language, most likely Python or Javascript, may notice that Go does not have a class, only structs. Some might wonder how structs differ from classes. It turns out that they are actually pretty similar! For our purposes, at least.
- Both are used to group together different data into a single logically coherent object.
- Both have fields that can be private and public. Do keep in mind that there are no private and public keywords in Go. To make a field (or a struct itself) public, you just need to capitalize the first letter of its name. For example, Laptop instead of laptop and Cpu instead of cpu.
- Both have access to a set of defined methods. Methods can be private and public as well, using the method (haha) above.
And honestly, that's all we need to know about. We could go into much more depth regarding the similarities and differences, but these three points drive the point home: for our intents and purposes, they basically accomplish the same thing.
What are methods?
Methods are like functions, but they are bound to a certain type or object, which are called receivers. I honestly don't know why they are called methods, and it's probably because engineers are bad at naming things.
Receivers do NOT have to be structs. They could be other types such as int, string, or even interface.
Let's write a method for our laptop struct defined above. Say that we want to upgrade our storage. We can write a method like this:
func (l laptop) upgradeStorage(size int) {
l.storage += size
}
Seems pretty reasonable, right? It looks exactly the same as a normal function, but it has one key difference. Look at the part between func and upgradeStorage. The part (l laptop) is where we define this method's receiver. An object l that is of the type laptop can call upgradeStorage. We can access l's fields by using a dot. Here, we are editing its storage by accessing it through l.storage.
So what happens when we run this?
func main() {
thinkpad := laptop{"i5-1240p", 16, 256, "Lenovo"}
fmt.Println(thinkpad.storage)
thinkpad.upgradeStorage(100)
fmt.Println(thinkpag.storage)
}
256
256
Interesting. The storage didn't change at all! Why would this be the case?
The astute readers may have already noticed, but this has to do with the concept of pass-by-value and pass-by-reference. Pass-by-value means that the method is working on a separate copy of the receiver. Pass-by-reference means that the method is working on a reference to the receiver.
In this context, this means that when we call thinkpad.upgradeStorage(), Go is actually creating an identical copy of thinkpad, then applying changes to that copy instead of the original. Why does it do it? It's for safety reasons, mostly. If you didn't want to change a field value in a struct, but have the power to do so, there is a chance that you'd be overwriting an important piece of data. Go only supports pass-by-value by design. When you know you want to change the field value, you must explicitly state it. How? By using pointer receivers.
func (l *laptop) upgradeStorage(size int) {
l.storage += size
}
Do you notice the change? The receiver is now of type *laptop, which is a pointer to the struct laptop. This way, the method can access the memory location of l and actually change the original object. We aren't playing around with copies anymore. Doing it this way has the added benefit of making the code run faster because Go doesn't have to spend time making an identical copy of l.
Now let's try running the code again.
256
356
Nice.
What about getters and setters?
Short answer: if you need it, do it.
For those of you who may be unfamiliar with the concept, it's common in many other languages to define a getter and a setter method.
- A getter method will get the value of a field.
- A setter method will set the value of a field.
For instance, if we were to write this in Go, we would do something like this:
func (l *laptop) getCpu() string {
return l.cpu
}
func (l *laptop) setCpu(newCpu string) {
l.cpu = newCpu
}
getCpu returns the cpu value of l, and setCpu updates l.cpu to whatever CPU we pass as newCpu.
Why do this? Well, people wanted to limit access to some fields, and encapsulate them so that people can only access the fields via getters and setters.
Go doesn't provide this out of the box, and if you wish to use one, you must write one yourself. There are many reasons why you would want an accessor like this, but always make sure that you aren't overengineering anything. Sometimes, just accessing the struct field is ok. There's a whole debate on this topic in the Go community, as well, which you can check out if you are curious.
One more thing: if you do choose to write a getter, don't include the word get. For example, instead of getCpu(), use Cpu().
Conclusion
Thank you for tuning in again this week! This is a simple topic, but I realized that I haven't touched on it specifically, and because Go is a rather nuanced language, some people may not be used to the Go way of doing things. Hopefully, this post cleared up some questions you had in mind.
You can also read this post on Dev.to.
Level Up Coding
Thanks for being a part of our community! Before you go:
- 👏 Clap for the story and follow the author 👉
- 📰 View more content in the Level Up Coding publication
- 🔔 Follow us: Twitter | LinkedIn | Newsletter
🚀👉 Join the Level Up talent collective and find an amazing job
Structs, Methods, and Receivers in Go was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Level Up Coding - Medium and was authored by Jacob Kim
Jacob Kim | Sciencx (2023-01-30T17:23:03+00:00) Structs, Methods, and Receivers in Go. Retrieved from https://www.scien.cx/2023/01/30/structs-methods-and-receivers-in-go/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.