Go Structures

A structure, usually called a struct in Go, is a user-defined type that groups related fields under one name. Each field can have a different datatype. For example, a student struct can contain a name as a string, a roll number as an integer, and an active status as a Boolean value.

Go structs are useful for representing records and application data such as users, products, configuration values, API responses, and database rows. Unlike classes in some object-oriented languages, a Go struct does not define inheritance. Behavior can instead be associated with a struct by declaring methods with receiver parameters.

In this tutorial, you will learn how to define a struct, declare and initialize struct variables, access fields, work with pointers, attach methods, use struct tags, embed structs, and distinguish structs from interfaces.

Declare a Struct Type in Go

To declare a structure in Go, use the keywords type and struct. Field declarations are written inside braces. Go code normally uses a descriptive type name in PascalCase when the type must be exported from its package.

The syntax to declare a structure is

</>
Copy
type struct_name struct {
   member definition;
   member definition;
   ...
   member definition;
}

The following is an example, to declare student structure datatype with properties: name and rollNumber.

example.go

</>
Copy
package main

type student struct {
   name string
   rollNumber int
}

func main() {

}

The field names in this example begin with lowercase letters, so they are available only within the same package. A field beginning with an uppercase letter, such as Name, is exported and can be accessed from another package.

Declare Variables of a Go Struct Type

After defining a struct type, you can declare variables of that type with the var keyword. A declared struct variable is initialized with the zero value of each field. For the student type below, name initially contains an empty string and rollNumber initially contains 0.

example.go

</>
Copy
package main

type student struct {
   name string
   rollNumber int
}

func main() {
	// declare variables of Student type
	var student1 student
	var student2 student
}

Initialize Go Struct Variables with Struct Literals

A struct literal creates a struct value and optionally supplies values for its fields. You can initialize a struct when declaring the variable or assign a struct value later.

example.go

</>
Copy
package main

type student struct {
   name string
   rollNumber int
}

func main() {
	// intialize during declaration
	var student1 = student{name:"A", rollNumber:14}
	var student2 student
	// intialize explicitly
	student2 = student{name:"B", rollNumber:13}
}

Using field names in a struct literal is generally clearer and safer because the values do not depend on the order of fields in the type definition. You can also omit fields that should retain their zero values.

</>
Copy
package main

import "fmt"

type Student struct {
	Name       string
	RollNumber int
	Active     bool
}

func main() {
	student := Student{
		Name:       "Maya",
		RollNumber: 21,
	}

	fmt.Println(student)
}

Output

{Maya 21 false}

The Active field was not included in the literal, so it received the zero value false.

Access and Update Go Struct Fields

To access members of a structure, use dot . operator. You can access a specific member by writing structure variable, followed by dot and then the member variable name.

In the following example, we have used dot operator to set the value of member of the structure variable and also to get the value of a member.

example.go

</>
Copy
package main

import "fmt"

type student struct {
   name string
   rollNumber int
}

func main() {
	var student1 = student{name:"Anu", rollNumber:14}
	// get the member value
	fmt.Printf("student1 details\n--------------\n")
	fmt.Printf("Name : %s\n", student1.name)
	fmt.Printf("Rollnumber : %d\n", student1.rollNumber)
	
	var student2 = student{name:"Arjit", rollNumber:13}
	// set the member value
	student2.rollNumber=24
	student2.name = "Bungi"
	
	fmt.Printf("\nstudent2 details\n--------------\n")
	fmt.Printf("Name : %s\n", student2.name)
	fmt.Printf("Rollnumber : %d\n", student2.rollNumber)
}

Output

student1 details
--------------
Name : Anu
Rollnumber : 14

student2 details
--------------
Name : Bungi
Rollnumber : 24

Use Pointers to Modify Go Struct Values

Assigning one struct variable to another normally copies the complete struct value. Changes made to the copy do not affect the original variable. Use a pointer when a function or method needs to update the original struct or when copying a large struct would be unnecessary.

</>
Copy
package main

import "fmt"

type Student struct {
	Name       string
	RollNumber int
}

func updateRollNumber(student *Student, number int) {
	student.RollNumber = number
}

func main() {
	student := Student{Name: "Maya", RollNumber: 21}
	updateRollNumber(&student, 25)

	fmt.Println(student.RollNumber)
}

Output

25

Go automatically dereferences a struct pointer when you access a field with dot notation. Therefore, student.RollNumber can be used instead of writing (*student).RollNumber.

Define Methods on Go Struct Types

You can associate behavior as well to structs, in addition to properties, in the form of functions. In the following example, we have a struct defined, and an associated method to the Student struct.

example.go

</>
Copy
package main

import "fmt"

// struct definition
type Student struct {
   name string
   rollNumber int
}

// associate method to Strudent struct type
func (s Student) PrintDetails() {
	fmt.Println("Student Details\n---------------")
	fmt.Println("Name :", s.name)
	fmt.Println("Roll Number :", s.rollNumber)
}

func main() {
	var stud1 = Student{name:"Anu", rollNumber:14} 
	stud1.PrintDetails()
}

Output

Student Details
---------------
Name : Anu
Roll Number : 14

The identifier between func and the method name is the receiver. A value receiver, such as (s Student), receives a copy of the struct. A pointer receiver, such as (s *Student), can modify the original value.

Update Struct Fields with a Pointer Receiver Method

</>
Copy
package main

import "fmt"

type Student struct {
	Name       string
	RollNumber int
}

func (s *Student) ChangeRollNumber(number int) {
	s.RollNumber = number
}

func main() {
	student := Student{Name: "Maya", RollNumber: 21}
	student.ChangeRollNumber(25)

	fmt.Println(student.RollNumber)
}

Output

25

Create Anonymous Structs for Temporary Data

An anonymous struct is declared without defining a separate named type. It is useful for small, local values that are used only once, such as test data or a temporary grouping of fields.

</>
Copy
package main

import "fmt"

func main() {
	book := struct {
		Title string
		Pages int
	}{
		Title: "Go Basics",
		Pages: 240,
	}

	fmt.Println(book.Title, book.Pages)
}

Output

Go Basics 240

Store Go Struct Values in a Slice

A slice of structs is a common way to maintain a variable-length collection of records. Each item in the slice has the same struct type.

</>
Copy
package main

import "fmt"

type Student struct {
	Name       string
	RollNumber int
}

func main() {
	students := []Student{
		{Name: "Maya", RollNumber: 21},
		{Name: "Ravi", RollNumber: 22},
		{Name: "Neha", RollNumber: 23},
	}

	for _, student := range students {
		fmt.Printf("%s: %d\n", student.Name, student.RollNumber)
	}
}

Output

Maya: 21
Ravi: 22
Neha: 23

Embed Structs to Compose Related Go Types

Struct embedding places one type inside another struct without assigning an explicit field name. The embedded type’s exported fields and methods are promoted, so they can be accessed through the outer struct. Embedding supports composition; it is not class inheritance.

</>
Copy
package main

import "fmt"

type Address struct {
	City    string
	Country string
}

type Employee struct {
	Name string
	Address
}

func main() {
	employee := Employee{
		Name: "Maya",
		Address: Address{
			City:    "Pune",
			Country: "India",
		},
	}

	fmt.Println(employee.Name)
	fmt.Println(employee.City)
	fmt.Println(employee.Address.Country)
}

Output

Maya
Pune
India

Use Go Struct Tags with JSON Encoding

A struct tag is metadata attached to a field declaration. Packages can inspect tags through reflection and use them to control encoding, validation, database mapping, or other behavior. The encoding/json package, for example, reads json tags to determine object property names.

</>
Copy
package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name       string `json:"name"`
	RollNumber int    `json:"roll_number"`
	Password   string `json:"-"`
}

func main() {
	student := Student{
		Name:       "Maya",
		RollNumber: 21,
		Password:   "secret",
	}

	data, err := json.Marshal(student)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(string(data))
}

Output

{"name":"Maya","roll_number":21}

The tag json:"-" tells the JSON encoder to omit the Password field. Struct tags are string literals and must use the format expected by the package that reads them.

Compare Go Struct Values

Two struct values can be compared with == and != when every field in the struct is itself comparable. Strings, numbers, Boolean values, pointers, channels, interfaces, and arrays of comparable values are comparable. Slices, maps, and functions are not comparable, so a struct containing one of those field types cannot be compared directly.

</>
Copy
package main

import "fmt"

type Point struct {
	X int
	Y int
}

func main() {
	point1 := Point{X: 10, Y: 20}
	point2 := Point{X: 10, Y: 20}
	point3 := Point{X: 15, Y: 20}

	fmt.Println(point1 == point2)
	fmt.Println(point1 == point3)
}

Output

true
false

Go Struct vs Interface

A struct and an interface serve different purposes. A struct defines concrete data by listing fields. An interface defines required behavior by listing method signatures. A struct satisfies an interface automatically when its method set contains all methods required by that interface; no explicit declaration is needed.

</>
Copy
package main

import "fmt"

type Describer interface {
	Describe() string
}

type Product struct {
	Name string
}

func (p Product) Describe() string {
	return "Product: " + p.Name
}

func printDescription(value Describer) {
	fmt.Println(value.Describe())
}

func main() {
	product := Product{Name: "Keyboard"}
	printDescription(product)
}

Output

Product: Keyboard

Common Go Struct Mistakes

  • Using lowercase fields across packages: fields beginning with lowercase letters are not exported. Use uppercase field names when another package must access them.
  • Expecting assignment to share the same value: assigning a struct to another variable copies it. Use a pointer when both variables must refer to the same value.
  • Using a value receiver for a modifying method: changes made through a value receiver affect only its copy. Use a pointer receiver to update the original struct.
  • Depending on positional struct literals: literals without field names can break when the field order changes. Named fields are clearer for most application code.
  • Comparing structs with slice or map fields: such structs cannot be compared directly with ==.
  • Assuming embedding is inheritance: embedding promotes fields and methods, but it does not create an inheritance hierarchy.

Go Struct Frequently Asked Questions

What is a struct in Go?

A struct is a user-defined Go type that groups zero or more named fields. The fields can have different types and are stored together as one value.

How do I initialize a Go struct?

You can initialize a struct with a struct literal, such as Student{Name: "Maya", RollNumber: 21}. Fields omitted from a named-field literal receive their zero values.

When should a Go struct method use a pointer receiver?

Use a pointer receiver when the method must modify the original value, when the struct is large enough that copying should be avoided, or when receiver consistency requires the type’s methods to use pointers.

Can a Go struct contain another struct?

Yes. A struct may contain another struct as a named field or embed it as an anonymous field. Named fields make the relationship explicit, while embedding promotes the embedded type’s accessible fields and methods.

Can Go structs have methods?

Yes. A method is declared as a function with a receiver parameter whose base type is defined in the same package. Methods can use either value receivers or pointer receivers.

Go Struct Tutorial QA Checklist

  • Confirm that each struct literal uses field names where field order should not matter.
  • Verify that fields intended for use from another package begin with uppercase letters.
  • Check that methods modifying struct fields use pointer receivers.
  • Confirm that JSON tags use valid quoted tag syntax and match the expected property names.
  • Ensure struct comparisons are used only when every field type is comparable.
  • Run each Go example and verify that its displayed output matches the program.

Summary of Go Structures

Go structs provide a direct way to group related data into reusable types. You can initialize them with struct literals, access fields with dot notation, pass pointers when values must be modified, define methods with receivers, compose types through embedding, and attach metadata through struct tags. Interfaces complement structs by describing behavior rather than storing concrete fields.

In this Go Tutorial, we learned what a structure is in Go programming, and how to use it, with the help of examples.