A struct in Golang is a user defined data type which can contain multiple items in key-value pairs. It is a collection of related properties grouped into a single named type.
A simple example is an employee structure which contains fields such as name, age, address of an employee along with its values.

Once a struct is defined, you can create multiple variables having different values.

Declaring struct
A struct is a user defined data type. As such, it is declared using type keyword.
Syntax is

type <struct_name> struct {
   property_name data_type 
   property_name data_type 
}

Here,
struct is a keyword which identifies that we are declaring a struct.
struct_name and property_name are user defined names of struct and its properties respectively.
data_type is the data type of the value a property will contain.

Note that there may be any number of properties in a struct and there is no separator between multiple properties.

Example of an employee struct is,

type employee struct {
   firstName string
   lastName string
   age int
}

If multiple properties have the same data type, they can be declared in the same line without repeating the data type as

type employee struct {
   firstName, lastName string
   age int
}

In this case, property names should be separated by comma.
Create struct
Once a struct is declared and defined, you can create variables of its type having different values.
There are two ways to create or initialize a struct variable
1. With only values
Supply values to struct properties as

var emp = employee{"ABC", "XYZ", 23}

// short form
emp := employee{"ABC", "XYZ", 23}

Property values are initialized in the order in which they appear in declaration.
This syntax has a disadvantage that if the order of properties in struct definition changes, then it needs to be updated in initialization as well.
2. With property names and values
In this method, property names are used along with their values separated by a colon as shown below.

var emp = employee{firstName:"ABC", lastName:"XYZ", age:23}

// short form
emp := employee{firstName:"ABC", lastName:"XYZ", age:23}

Changing the order of properties in struct definition has no effect on its values.

Accessing struct properties
Properties of a struct can be accessed using struct name and the name of property separated by a dot(.) as shown below.

// create a struct
emp := employee{firstName:"ABC", lastName:"XYZ", age:23}

// get the first name 
fn := emp.firstName
ln := emp.lastName
age := emp.age

Printing struct
A struct when printed outputs the values of all its properties separated by a space enclosed between curly braces. Example,

// create a struct
emp := employee{firstName:"ABC", lastName:"XYZ", age:23}

fmt.Println(emp)

This will print

{ABC XYZ 23}

struct example
Below is a complete example showing how to define a struct, initialize struct variable, access and modify its properties.

package main

import (
  "fmt"
)
// define struct
type employee struct {
  firstName string
  lastName  string
  age       int
}

func main() {
  // create struct
  emp := employee{firstName: "ABC", lastName: "XYZ", age: 23}
  // print its properties
  fmt.Println("Employee Name:", emp.firstName+" "+emp.lastName)
  fmt.Println("Employee Age:", emp.age)
  // modify property value
  emp.age = 24
  fmt.Println("Updated employee Age:", emp.age) 
  // print struct
  fmt.Println("Employee struct:",emp)
}

This prints

Employee Name: ABC XYZ
Employee Age: 23
Updated employee Age: 24
Employee struct: {ABC XYZ 24}

Anonymous struct
An anonymous struct is a struct without a name. Anonymous structs are defined at the point where they are needed.
These should be used when a struct is required only once.
Anonymous struct combines the definition of fields and their values in two separate blocks within curly braces.
Anonymous struct is created using only the struct keyword followed by the definition of fields that it will contain and the value of those fields, both in separate blocks. Example,

package main

import (
  "fmt"
)

func main() {

  emp := struct {
    firstName, lastName string
    age                 int
  }{firstName: "ABC", lastName: "XYZ", age: 23}
  fmt.Println("Employee Name:", emp.firstName+" "+emp.lastName)
  fmt.Println("Employee Age:", emp.age)
}

Note that anonymous structs do not need to be defined at the top.
Functions in struct
You can also define a function as a struct field in Go. This makes a struct more dynamic since it enables it to perform computations or processing power as well.
For using a function as struct field,
1. a function needs to be defined before struct definition using type keyword.
Function definition includes name of the function, data types of parameters that it will accept and its return type.
This definition does not have names of parameters, only their data types.

2. struct definition needs to have a field, that is bound to the function name.

Function body is written while initializing struct. It is written in the same way as a function is defined in Go but without a name.
It contains func keyword with the name and types of parameters, return type of function along with the logic that the function should perform.
Example,

package main

import (
  "fmt"
)

// define function
type getName func(string, string) string

// define struct
type employee struct {
  firstName, lastName string
  age                 int
  name             getName
}

func main() {
        // create struct with function
  emp := employee{"ABC", "XYZ", 23, func(fn string, ln string) string { return fn + " " + ln }}
  // print struct
  fmt.Println("Employee struct:", emp)
  // invoke struct function
  fmt.Println("Employee name:", emp.name(emp.firstName, emp.lastName))
}

Notice how the function is invoked using struct field which is of type function. This outputs

Employee struct: {ABC XYZ 24 0x499200}
Employee name: ABC XYZ

Note that the last value when the struct is printed is the memory address of function.
Nested struct
A struct may contain another struct as a field and this is called struct nesting or embedding struct.
For embedding a struct, first it needs to be defined using type just as we defined till now and then a field is added in the main or outer struct whose data type is the name of the struct that we defined earlier. Example,

package main

import (
  "fmt"
)
// define inner struct
type empAddress struct {
  city, country string
  pin           int
}

// embedding struct 
type employee struct {
  firstName, lastName string
  age                 int
  address             empAddress
}

func main() {
  // create inner struct
  address := empAddress{"Sydney","Australia",2063}
  // embedded struct
  emp := employee{"ABC","XYZ", 23, address}
  fmt.Println("Employee struct:", emp)
}

Notice how a struct is defined and used inside another struct. This example prints

Employee struct: {ABC XYZ 24 {Sydney Australia 2063}}

Embedded struct can be initialized in the same line as

emp := employee{"ABC","XYZ", 23, empAddress{"Sydney","Australia",2063}}

Accessing nested struct fields
In order to access the fields of the inner struct, first we need to access the inner struct property and then the required properties of the inner struct.
This requires using dot multiple times as shown below.

package main

import (
  "fmt"
)
// define inner struct
type empAddress struct {
  city, country string
  pin           int
}

// embedding struct 
type employee struct {
  firstName, lastName string
  age                 int
  address             empAddress
}

func main() {
  // create inner struct
  add := empAddress{"Sydney","Australia",2063}
  // embedded struct
  emp := employee{"ABC","XYZ", 23, add}
  fmt.Println("Employee country:", emp.add.country)
  fmt.Println("Employee city:", emp.add.city)
  fmt.Println("Employee pin:", emp.add.pin)
}

which prints

Employee country: Australia
Employee city: Sydney
Employee pin: 2063