Skip to content

Beginner · Collections and structs · topic 15 of 46 · 40 min

Structs and methods

Explanation

A struct groups related fields into one type, like a Student with Name, Roll and Marks. A method is a function attached to a type. A value receiver works on a copy; a pointer receiver can change the original. Go has no classes or inheritance; it uses embedding to reuse fields and methods.

Why structs?

A student has a name, a roll number and marks. Keeping them in three separate variables, or three parallel slices, gets messy fast. A struct is a type that groups named fields, so one value holds the whole record.

go
type Student struct {
	Name  string
	Roll  int
	Marks float64
}

func main() {
	s := Student{Name: "Asha", Roll: 23, Marks: 91.5}
	fmt.Println(s)          // {Asha 23 91.5}
	fmt.Printf("%+v\n", s)  // {Name:Asha Roll:23 Marks:91.5}
	fmt.Println(s.Name)     // Asha
	s.Marks = 93
	var empty Student
	fmt.Printf("%+v\n", empty) // {Name: Roll:0 Marks:0}
}
Fields are reached with a dot. Unset fields get their zero values. %+v prints field names.
Tip: Always use field names in struct literals (Student{Name: "Asha"}). The short form Student{"Asha", 23, 91.5} breaks as soon as someone adds or reorders a field.

Pointers to structs

Structs are values: assigning or passing one copies all its fields. To share one struct, use a pointer (the address of the value, covered fully in module 4). Go lets you use the dot directly on a pointer, without writing (*p).Name.

go
a := Student{Name: "Ravi", Marks: 70}
b := a          // copy
b.Marks = 99
fmt.Println(a.Marks) // 70

p := &a         // pointer to a
p.Marks = 80    // same as (*p).Marks = 80
fmt.Println(a.Marks) // 80
fmt.Println(p)       // &{Ravi 0 80}
Println shows a pointer to a struct with an & in front.

Methods

A method is a function with a receiver: a special parameter written before the name. It lets you call s.Grade() instead of grade(s). You can define methods on any named type you declare in your package, not only structs.

go
func (s Student) Grade() string {   // value receiver
	if s.Marks >= 90 {
		return "A"
	}
	return "B"
}

func (s *Student) AddGrace(n float64) { // pointer receiver
	s.Marks += n
}

func main() {
	st := Student{Name: "Meena", Marks: 88}
	st.AddGrace(2)              // Go passes &st automatically
	fmt.Println(st.Marks, st.Grade()) // 90 A
}
AddGrace changes the original because it has a pointer receiver.

Value or pointer receiver?

Use a pointer receiver (*T) whenA value receiver (T) is fine when
The method changes the receiverThe method only reads
The struct is large (copying is wasteful)The type is small, like a Point or a Money amount
The struct contains a sync.Mutex or similar that must not be copiedThe type is a basic type such as type Celsius float64
Other methods of the type already use pointers (be consistent)You want the value to behave like an immutable value
Careful: Classic bug: a method with a VALUE receiver that tries to change a field. func (s Student) AddGrace(n float64) { s.Marks += n } changes a copy, and the caller sees no change. If it should change, use *Student.

Constructor functions

Go has no constructors. By convention you write a function named NewSomething that checks the inputs and returns a ready value, often a pointer.

go
type Account struct {
	Holder  string
	balance int64 // lowercase: hidden outside the package
}

func NewAccount(holder string) (*Account, error) {
	if holder == "" {
		return nil, errors.New("holder name required")
	}
	return &Account{Holder: holder}, nil
}

func (a *Account) Deposit(paise int64) { a.balance += paise }
func (a *Account) Balance() int64     { return a.balance }
Lowercase fields cannot be touched from other packages, so all changes go through methods.

Embedding: reuse without inheritance

Go has no inheritance. Instead you can embed one struct type inside another by writing just the type name as a field. The embedded type's fields and methods are promoted: you can use them directly on the outer struct. This is composition: an Employee HAS a Person, it is not a kind of Person.

go
type Person struct {
	Name string
}

func (p Person) Hello() string { return "Namaste, " + p.Name }

type Employee struct {
	Person          // embedded
	EmpID  string
	Salary int
}

func main() {
	e := Employee{Person: Person{Name: "Kiran"}, EmpID: "E42"}
	fmt.Println(e.Name)    // Kiran (promoted field)
	fmt.Println(e.Hello()) // Namaste, Kiran (promoted method)
}
You can still reach the inner value as e.Person.

Comparing structs and struct tags

Two structs of the same type can be compared with == if all their fields are comparable (no slices or maps). A struct tag is a small text label after a field in backticks. Packages like encoding/json read tags to know the JSON name of a field. You will use them in module 7.

go
type Product struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Price int    `json:"price_paise"`
}
Tags do nothing by themselves; a package must read them.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Structs and methods · 1/7

Structs and methods

Structs group related fields into one type, like a student record.

Prefer a video by a teacher? Find free videos on “golang structs methods pointer receiver vs value receiver embedding” on YouTube ↗

In real life: A bank account with rules

A wallet app models each account as a struct. The balance field is lowercase, so no other package can set it directly; money only moves through Deposit and Withdraw methods that check the rules, such as "you cannot withdraw more than the balance". This is how Go code protects important data without classes.

go
package main

import (
	"errors"
	"fmt"
)

type Wallet struct {
	Owner   string
	balance int64 // paise
}

func (w *Wallet) Deposit(p int64) { w.balance += p }

func (w *Wallet) Withdraw(p int64) error {
	if p > w.balance {
		return errors.New("insufficient balance")
	}
	w.balance -= p
	return nil
}

func (w Wallet) String() string {
	return fmt.Sprintf("%s: Rs %d.%02d", w.Owner, w.balance/100, w.balance%100)
}

func main() {
	w := &Wallet{Owner: "Asha"}
	w.Deposit(50000)
	if err := w.Withdraw(70000); err != nil {
		fmt.Println("error:", err)
	}
	_ = w.Withdraw(12550)
	fmt.Println(w) // Asha: Rs 374.50
}
The String method makes fmt print the wallet nicely (you will see why in module 4).

Practice (0/4 done)

Task 1 · Question

What does this print? type P struct{ Name string; Age int } fmt.Printf("%+v", P{"Asha", 21})

Task 2 · Question

What does this print? type C struct{ n int } func (c C) Inc() { c.n++ } ... x := C{} x.Inc() x.Inc() fmt.Println(x.n)

Task 3 · Question

type Animal struct{ Name string }; type Dog struct{ Animal; Breed string }. d := Dog{Animal{"Moti"}, "Indie"}. What is d.Name?

Task 4 · Practice

Define type Rectangle struct with Width and Height (float64). Add a value-receiver method Area() float64 and a pointer-receiver method Scale(f float64) that multiplies both sides. Show that Scale changes the rectangle.

Quick quiz

Question 1

1.What does assigning one struct variable to another do?

Question 2

2.When should a method use a pointer receiver?

Question 3

3.How does Go reuse code from another struct type?

Question 4

4.What does a field named balance (lowercase) mean?

Question 5

5.What is the Go convention for a constructor?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does assigning one struct variable to another do?

Answer: Copies all the fields

Structs are values; assignment copies every field.

When should a method use a pointer receiver?

Answer: When it must change the receiver or the struct is large

Pointer receivers let methods modify the original and avoid copying big structs.

How does Go reuse code from another struct type?

Answer: Embedding (composition)

Embedding promotes the inner type's fields and methods to the outer type.

What does a field named balance (lowercase) mean?

Answer: It cannot be accessed from other packages

Lowercase identifiers are unexported: visible only inside their own package.

What is the Go convention for a constructor?

Answer: A function named NewTypeName

Go uses plain functions like NewAccount that return a ready value.

Can you assign one struct to another of the same type with =?Related

Answer: Yes, all members are copied

Struct assignment copies everything.

From Structs

After let b = Student { roll: 2, ..a }; where a has String fields, what is true?Related

Answer: a's String fields were moved into b

Struct update syntax moves non-Copy fields.

From Structs: your own data types

Can a struct contain a member of its own struct type (not a pointer)?Related

Answer: No: it would be infinitely large; it can contain a pointer to its type

Self-reference must be through a pointer.

From Pointers to structs and the arrow operator

When is a struct a good choice?Related

Answer: For small, immutable, value-like data such as a point or money

Small, immutable values are the classic struct use.

From Structs, value types and reference types

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Collections and structs”. The module exam comes next.
  2. Module 3 exam: Collections and structs · 15 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Pointers, interfaces and errors. 46 topics left in the course before the final exam.

← Previous: Maps: key-value look-ups