# Structs and Methods

Course: [Go](https://www.devtutorials.ai/learn/go) · Section: Types and OOP
Source: https://www.devtutorials.ai/learn/go/types-and-oop/structs-and-methods

> 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:

1. What structs are, why they are used, and how to define them.
2. How to create struct values, by position or by field name.
3. How to read, update, compare and zero struct fields.
4. What methods are, and the difference between value and pointer receivers.
5. How to write constructor functions.
6. Anonymous structs and struct tags.

## Struct Fundamentals

### Why Structs Are Used

- **Keep related data together.** No separate `name`, `class`, `marks` variables 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

```go
type Student struct {
	Name   string
	Class  string
	Marks  int
	Hostel bool
}
```

- `type Student struct` makes a new type named `Student`.
- 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.

```go runnable title="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)
}
```

```text
{Asha 8A 91}
{Name:Asha Class:8A Marks:91}
```

- `fmt.Println` prints the values in order.
- `%+v` also 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

### Positional Initialization

```go runnable title="Positional"
package main

import "fmt"

type Point struct {
	X, Y int
}

func main() {
	p := Point{3, 4}
	fmt.Println(p)
}
```

```text
{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

```go runnable title="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)
}
```

```text
{Name:Ravi Class: Marks:78 Hostel:false}
```

- `Class` was left out, so it is `""`.
- `Hostel` was left out, so it is `false`.

**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`.

```go runnable title="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)
}
```

```text
Meena 80
{Meena Iyer 85}
```

### Zero Values in Structs

```go runnable title="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)
}
```

```text
{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**.

```go runnable title="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)
}
```

```text
91 50
```

- `b := a` copies 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.

```go runnable title="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})
}
```

```text
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.

```go
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.

```go runnable title="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())
}
```

```text
Ravi C
Ravi 77 B
```

- `ravi.AddGrace(5)` works even though `ravi` is 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.

```go runnable title="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)
}
```

```text
{Name:Kavya Class:8C Marks:88}
```

- `NewStudent` fills 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.

```go runnable title="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)
}
```

```text
{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.

```go runnable title="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)
	}
}
```

```text
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.

```go
type Student struct {
	Name  string `json:"name"`
	Marks int    `json:"marks"`
}
```

- Tags do nothing by themselves.
- Packages like `encoding/json` read them.

### JSON Tags

```go runnable title="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))
}
```

```text
{"name":"Asha","class":"8A","marks":91}
```

- `json:"name"` = use `name` as the key, not `Name`.
- `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**.

```go runnable title="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"))
	}
}
```

```text
Name → Student name
Phone → Parent phone
```

- `f.Tag.Get("label")` reads the value of the `label` tag.
- 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

1. **Struct** = `type Student struct { Name string; Marks int }`.
2. **Create** = `Student{Name: "Asha", Marks: 91}` (named), `Student{"Asha", 91}` (positional), `var s Student` (zero).
3. **Fields** = `s.Name`. Assigning copies the struct. `==` compares all fields.
4. **Method** = `func (s Student) Grade() string`.
   - **Value receiver** = reads a copy.
   - **Pointer receiver** = `(s *Student)`, can change the struct.
5. **Constructor** = `func NewStudent(...) *Student`, with defaults and checks.
6. **Anonymous struct** = `struct{ ... }{ ... }` for one-time use.
7. **Struct tag** = `` `json:"name"` ``, read by packages like `encoding/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 `NewX` functions 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.
