Structs and Methods
By Syncfusion®, Inc. · Last updated
Define structs, create and fill them, read, update and compare fields, add methods with value and pointer receivers, write constructors, and use anonymous structs and struct tags.
What Are Structs?
Struct = your own type that groups related values together, each with a name.
Field = one named value inside a struct.
A student has many details:
- a name (text)
- a class (text)
- marks (a number)
- a hostel flag (true or false)
A struct keeps all of them in one variable.
Simple lesson: a struct is a record with named fields, like one row of a register.
A Real-World Example
Think of the admission form every student fills in.
- The blank form has the same boxes for everyone: Name, Class, Roll No.
- Each filled form has different values in those boxes.
- The school keeps one filled form per student.
Now the mapping:
- The blank form = the struct type.
- Each box on the form = a field.
- A filled form = a struct value, called an instance.
Go has no classes.
- Structs hold the data.
- Methods add behaviour to a type.
- Together, they do the job that classes do in other languages.
What You Will Learn
In this lesson, you will learn:
- What structs are, why they are used, and how to define them.
- How to create struct values, by position or by field name.
- How to read, update, compare and zero struct fields.
- What methods are, and the difference between value and pointer receivers.
- How to write constructor functions.
- Anonymous structs and struct tags.
Struct Fundamentals
Why Structs Are Used
- Keep related data together. No separate
name,class,marksvariables to keep in step. - Pass one thing to a function instead of five.
- Make lists of records like
[]Student. See Collections of Structs. - Give the data behaviour with methods.
Defining Structs
type Student struct {
Name string
Class string
Marks int
Hostel bool
}
type Student structmakes a new type namedStudent.- Each line inside is a field: a name, then a type.
- Fields with the same type can share a line:
X, Y int.
Creating Struct Instances
Instance = one value of a struct type.
Make a student
package main
import "fmt"
type Student struct {
Name string
Class string
Marks int
}
func main() {
asha := Student{Name: "Asha", Class: "8A", Marks: 91}
fmt.Println(asha)
fmt.Printf("%+v\n", asha)
}
{Asha 8A 91}
{Name:Asha Class:8A Marks:91}
fmt.Printlnprints the values in order.%+valso prints the field names. It is handy for checking.
Simple lesson: define a struct with type Name struct { ... }, then fill in an instance with its values.
Struct Initialization
Three ways to fill a struct
Positional Initialization
Positional
package main
import "fmt"
type Point struct {
X, Y int
}
func main() {
p := Point{3, 4}
fmt.Println(p)
}
{3 4}
- Fine for tiny structs like a point.
- For bigger structs, use field names.
Positional values must follow the field order exactly.
If someone later adds or reorders fields, every positional literal breaks or, worse, fills the wrong fields. Prefer field names.
Named Field Initialization
Named fields, some left out
package main
import "fmt"
type Student struct {
Name string
Class string
Marks int
Hostel bool
}
func main() {
ravi := Student{Name: "Ravi", Marks: 78}
fmt.Printf("%+v\n", ravi)
}
{Name:Ravi Class: Marks:78 Hostel:false}
Classwas left out, so it is"".Hostelwas left out, so it isfalse.
Simple lesson: use field names when you fill a struct. Anything you leave out gets its zero value.
Struct Fields
Accessing and Updating Fields
Use a dot: variable.Field.
Read and change fields
package main
import "fmt"
type Student struct {
Name string
Marks int
}
func main() {
s := Student{Name: "Meena", Marks: 80}
fmt.Println(s.Name, s.Marks)
s.Marks += 5
s.Name = "Meena Iyer"
fmt.Println(s)
}
Meena 80
{Meena Iyer 85}
Zero Values in Structs
A zero-value struct
package main
import "fmt"
type Book struct {
Title string
Pages int
Borrowed bool
}
func main() {
var b Book
fmt.Printf("%+v\n", b)
b.Title = "Panchatantra"
fmt.Printf("%+v\n", b)
}
{Title: Pages:0 Borrowed:false}
{Title:Panchatantra Pages:0 Borrowed:false}
Structs Are Copied
A struct is a value type, like an array. See Arrays.
Copying a struct
package main
import "fmt"
type Student struct {
Name string
Marks int
}
func main() {
a := Student{"Asha", 91}
b := a
b.Marks = 50
fmt.Println(a.Marks, b.Marks)
}
91 50
b := acopies every field.- To share one struct, use a pointer. See Pointers.
Struct Comparison
Two structs can be compared with == when all their fields can be compared.
Compare structs
package main
import "fmt"
type Seat struct {
Row, Col int
}
func main() {
mine := Seat{2, 5}
yours := Seat{2, 5}
fmt.Println(mine == yours)
fmt.Println(mine == Seat{3, 5})
}
true
false
==is true when every field is equal.- A struct with a slice or map field cannot use
==.
Simple lesson: read and change fields with a dot, remember that assigning copies the struct, and compare with ==.
Methods
What Are Methods?
Method = a function that belongs to a type.
Receiver = the value the method is called on. It is written before the method name.
func (s Student) Report() string {
return s.Name + ": " + strconv.Itoa(s.Marks)
}
(s Student)is the receiver.- You call it with a dot:
asha.Report().
Value Receivers
Value receiver = (s Student). The method gets a copy.
- Use it when the method only reads the struct.
Pointer Receivers
Pointer receiver = (s *Student). The method gets a pointer.
- Use it when the method changes the struct.
- Also use it for big structs, to avoid copying.
Press Play and compare the two methods.
Value receiver vs pointer receiver
A report card with methods
package main
import "fmt"
type Student struct {
Name string
Marks int
}
func (s Student) Grade() string {
if s.Marks >= 90 {
return "A"
}
if s.Marks >= 75 {
return "B"
}
return "C"
}
func (s *Student) AddGrace(n int) {
s.Marks += n
}
func main() {
ravi := Student{"Ravi", 72}
fmt.Println(ravi.Name, ravi.Grade())
ravi.AddGrace(5)
fmt.Println(ravi.Name, ravi.Marks, ravi.Grade())
}
Ravi C
Ravi 77 B
ravi.AddGrace(5)works even thoughraviis not a pointer. Go writes(&ravi).AddGrace(5)for you.
Easy way to remember
- Method reads → value receiver
(s Student) - Method changes → pointer receiver
(s *Student) - If one method of a type needs a pointer, give all its methods pointer receivers, for consistency.
Simple lesson: a method is a function with a receiver, and a pointer receiver lets it change the struct.
Constructors
Factory Functions
Go has no special constructor keyword.
Constructor (or factory function) = a normal function, usually named NewX, that builds and returns a ready-to-use value.
NewStudent
package main
import "fmt"
type Student struct {
Name string
Class string
Marks int
}
func NewStudent(name, class string) *Student {
return &Student{Name: name, Class: class, Marks: 0}
}
func main() {
s := NewStudent("Kavya", "8C")
s.Marks = 88
fmt.Printf("%+v\n", *s)
}
{Name:Kavya Class:8C Marks:88}
NewStudentfills in the fields and gives back a pointer.- Returning a pointer to a local value is safe in Go. The value lives on as long as it is used.
Initialization Patterns
- Set defaults that are not zero values, like
Class: "8A". - Check inputs, and return an error if they are wrong. See Error Handling.
- Return a pointer (
*T) when the value will be changed by methods.
A constructor with a default and a check
package main
import (
"errors"
"fmt"
)
type Locker struct {
Number int
Owner string
}
func NewLocker(number int, owner string) (*Locker, error) {
if number < 1 || number > 200 {
return nil, errors.New("locker number must be 1 to 200")
}
if owner == "" {
owner = "unassigned"
}
return &Locker{Number: number, Owner: owner}, nil
}
func main() {
l, err := NewLocker(12, "")
fmt.Println(*l, err)
_, err = NewLocker(500, "Ravi")
fmt.Println(err)
}
{12 unassigned} <nil>
locker number must be 1 to 200
Simple lesson: write a NewX function to build a value with good defaults and checks.
Anonymous Structs
Inline Structs
Anonymous struct = a struct with no type name, made and used in one place.
An anonymous struct
package main
import "fmt"
func main() {
topper := struct {
Name string
Marks int
}{"Kabir", 98}
fmt.Println(topper.Name, topper.Marks)
scores := []struct {
Subject string
Marks int
}{
{"Maths", 92},
{"Science", 88},
}
for _, s := range scores {
fmt.Println(s.Subject, s.Marks)
}
}
Kabir 98
Maths 92
Science 88
- Good for small, one-time groupings, like test data.
- If you use the shape in more than one place, give it a name with
type.
Simple lesson: an anonymous struct is a quick, nameless record for one-time use.
Struct Tags
Struct Tag Fundamentals
Struct tag = extra text after a field, in back-quotes. Packages read it to know how to handle the field.
type Student struct {
Name string `json:"name"`
Marks int `json:"marks"`
}
- Tags do nothing by themselves.
- Packages like
encoding/jsonread them.
JSON Tags
JSON field names from tags
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Name string `json:"name"`
Class string `json:"class"`
Marks int `json:"marks"`
Remark string `json:"remark,omitempty"`
}
func main() {
s := Student{Name: "Asha", Class: "8A", Marks: 91}
data, _ := json.Marshal(s)
fmt.Println(string(data))
}
{"name":"Asha","class":"8A","marks":91}
json:"name"= usenameas the key, notName.omitempty= leave the field out when it is empty.- You will learn JSON fully in JSON.
Custom Tags
Your own code can read any tag with the reflect package. You will learn reflect in Reflection.
Read a custom tag
package main
import (
"fmt"
"reflect"
)
type Form struct {
Name string `label:"Student name"`
Phone string `label:"Parent phone"`
}
func main() {
t := reflect.TypeOf(Form{})
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Println(f.Name, "→", f.Tag.Get("label"))
}
}
Name → Student name
Phone → Parent phone
f.Tag.Get("label")reads the value of thelabeltag.- Validation and database packages use tags the same way.
Simple lesson: a struct tag is a label for other code, most often json:"name".
TL;DR
- Struct =
type Student struct { Name string; Marks int }. - Create =
Student{Name: "Asha", Marks: 91}(named),Student{"Asha", 91}(positional),var s Student(zero). - Fields =
s.Name. Assigning copies the struct.==compares all fields. Method =
func (s Student) Grade() string.- Value receiver = reads a copy.
- Pointer receiver =
(s *Student), can change the struct.
- Constructor =
func NewStudent(...) *Student, with defaults and checks. - Anonymous struct =
struct{ ... }{ ... }for one-time use. - Struct tag =
`json:"name"`, read by packages likeencoding/json.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| A value receiver that changes a field | Changes stick | The method changes a copy | The struct stays the same. |
| Positional values in the wrong order | Go matches by type | Go matches by position | Fields get the wrong values. |
| Leaving out a field in positional form | Missing ones become zero | Positional needs every field | too few values in struct literal |
b := a to share a struct | It shares | It copies | Changes to b are not seen in a. |
Lowercase field with encoding/json | All fields are saved | Only exported (Capital) fields | The field is missing in the JSON. |
== on a struct with a slice field | All structs compare | Only if every field is comparable | A build error. |
A tag with spaces: json: "name" | Spaces are fine | Write json:"name" exactly | The tag is ignored. |
Check Yourself
Exercises
What to Remember
- A struct groups named fields into your own type.
- Fill structs with field names. Missing fields get zero values.
- Assigning a struct copies it.
==compares all fields. - Methods have receivers. Use a pointer receiver to change the struct.
- Write
NewXfunctions for defaults and checks. - Struct tags like
json:"name"tell packages how to treat a field.
Next: Composition and Embedding. You will learn how Go builds bigger types from smaller ones, instead of using inheritance.