Structs in Golang

Topics to be covered in this article:
Structs
Nested structs
Anonymous structs
Nested anonymous structs
Embedded structs
Struct methods
We use structs in Go to group different types of variables together.
Structs in Go are often used to represent data that you might use a dictionary in PYTHON or object in JS
For example :
type car struct {
brand string
model string
doors int
mileage int
}
Usage :
package main
import "fmt"
type car struct {
model int
specs string
}
func info( c car) {
fmt.Printf("the car model %v has specs %s \n", c.model, c.specs)
}
func main() {
ford := car{model: 678, specs: "V8 engine"}
info(ford)
}
Nested Structs
Structs can be nested to represent more complex entities:
type car struct {
brand string
model string
doors int
mileage int
frontWheel wheel
backWheel wheel
}
type wheel struct {
radius int
material string
}
The fields of a struct can be accessed using the dot .
operator.
myCar := car{}
myCar.frontWheel.radius = 5
Usage :
package main
import "fmt"
type user struct {
name string
number int
}
type message struct {
message string
sender user
recipient user
}
func canSendMessage(m message) bool {
return m.sender.name != "" &&
m.sender.number != 0 &&
m.recipient.name != "" &&
m.recipient.number != 0
}
/*
The function canSendMesage should return true
only if the sender and recipient fields each contain a name and a number.
If any of the default zero values are present, return false instead.
*/
Anonymous Structs in Go
An anonymous struct is just like a normal struct, but it is defined without a name and therefore cannot be referenced elsewhere in the code.
To create an anonymous struct, just instantiate the instance immediately using a second pair of brackets after declaring the type:
myCar := struct {
brand string
model string
} {
brand: "Toyota",
model: "Camry",
}
Nesting with anonymous struct :
type car struct {
brand string
model string
doors int
mileage int
// wheel is a field containing an anonymous struct
wheel struct {
radius int
material string
}
}
var myCar = car{
brand: "Rezvani",
model: "Vengeance",
doors: 4,
mileage: 35000,
wheel: struct {
radius int
material string
}{
radius: 35,
material: "alloy",
},
}
In general, prefer named structs. Named structs make it easier to read and understand your code, and they have the nice side-effect of being reusable. I sometimes use anonymous structs when I know I won't ever need to use a struct again. For example, sometimes I'll use one to create the shape of some JSON data in HTTP handlers.
If a struct is only meant to be used once, then it makes sense to declare it in such a way that developers down the road won’t be tempted to accidentally use it again.
Embedded Structs
Go is not an object-oriented language. However, embedded structs provide a kind of data-only inheritance that can be useful at times. Keep in mind, Go doesn't support classes or inheritance in the complete sense, but embedded structs are a way to elevate and share fields between struct definitions.
type car struct {
brand string
model string
}
type truck struct {
// "car" is embedded, so the definition of a
// "truck" now also additionally contains all
// of the fields of the car struct
car
bedSize int
}
Embedded vs Nested :
Unlike nested structs, an embedded struct's fields are accessed at the top level like normal fields.
Like nested structs, you assign the promoted fields with the embedded struct in a composite literal.
lanesTruck := truck{
bedSize: 10,
car: car{
brand: "Toyota",
model: "Tundra",
},
}
fmt.Println(lanesTruck.brand) // Toyota
fmt.Println(lanesTruck.model) // Tundra
In the example above you can see that both brand
and model
are accessible from the top-level, while the nested equivalent to this object would require you to access these fields via a nested car
struct: lanesTruck.car.brand
or lanesTruck.car.model
.
Embedded structs are useful in backend code where we have pydantic models in python fastapi ,
similarly to store data during signup we create struct having user info and password too,
but after signup we just return success status code with same user info without password.
Struct Methods in Go
While Go is not object-oriented, it does support methods that can be defined on structs. Methods are just functions that have a receiver. A receiver is a special parameter that syntactically goes before the name of the function.
type rect struct {
width int
height int
}
// area has a receiver of (r rect)
// rect is the struct
// r is the placeholder
func (r rect) area() int {
return r.width * r.height
}
var r = rect{
width: 5,
height: 10,
}
fmt.Println(r.area())
// prints 50
A receiver is just a special kind of function parameter. In the example above, the r
in (r rect)
could just as easily have been rec
or even x
, y
or z
. By convention, Go code will often use the first letter of the struct's name.
Receivers are important because they will, as you'll learn in the exercises to come, allow us to define interfaces that our structs (and other types) can implement.
Another example:
package main
import "fmt"
type authenticationInfo struct {
username string
password string
}
func (a authenticationInfo) formatter() string{
return fmt.Sprintf("Authorization: Basic %v:%v",a.username,a.password)
}
func main() {
a := authenticationInfo{
username: "raj",
password: "1234",
}
fmt.Println(a.formatter())
}
Extra example codes on golang struct:
Example 1 :
package main
import "fmt"
type User struct {
Name string
Membership
}
type Membership struct{
Type string
limit int
}
func newUser(name string, mtype string) User {
switch mtype {
case "premium":
return User{
Name: name,
Membership: Membership{
Type: "premium",
limit: 1000,
},
}
default:
return User{
Name: name,
Membership: Membership{
Type: "basic",
limit: 100,
},
}
}
}
func main(){
fmt.Println("nice code")
}
Example 2 :
package main
type User struct {
Name string
Membership
}
type Membership struct {
Type string
MessageCharLimit int
}
func newUser(name string, membershipType string) User {
membership := Membership{Type: membershipType}
if membershipType == "premium" {
membership.MessageCharLimit = 1000
} else {
membership.Type = "standard"
membership.MessageCharLimit = 100
}
return User{Name: name, Membership: membership}
}
/*
Create a SendMessage method for the User struct.
It should take a message string and messageLength int as inputs.
If the messageLength is within the user's message character limit, return the original
message and true (indicating the message can be sent), otherwise, return an empty string
and false.
*/
func (u User) SendMessage(message string, messageLength int) (msg string, status bool){
if messageLength <= u.MessageCharLimit {
return message, true
}
return "",false
}
Subscribe to my newsletter
Read articles from Rajesh Gurajala directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
