Golang ProgramsGolang Programs

Struct

Go have the ability to declare and create own data types by combining one or more types, including both built-in and user-defined types. The declaration of new data type is constructed to provide the compiler with size and representation information, similar like built-in data types.

Structs are the only way to create concrete user-defined types in Go. Struct types are declared by composing a fixed set of unique fields. Each field in a struct is declared with a known type, which could be a built-in type or another userdefined type.

Struct Declaration

The syntax of a struct is as follows:

type identifier struct{
  field1 data_type
  field2 data_type
  field3 data_type
}

The struct type is composed by defining the keyword struct followed by a set of field declarations enclosed within curly brackets.

An example program that declares struct in golang:

package main

import "fmt"

type rectangle struct {
	length  float64
	breadth float64
	color	string
}

func main() {
	fmt.Println(rectangle{10.5,25.10,"red"})
}
golang struct

As an example, a struct named rectangle containing two fields of type float64 and a third field of type string.

Different fields in the same struct can have different data types.

Data type for each field can be of any type int, float, string etc....

Different ways of Struct Instantiation

1) Dot notation

A struct uses a selector expression (or dot notation) to access the values stored in fields.

The following code snippet illustrates struct instantiating:

package main

import "fmt"

type rectangle struct {
	length  int
	breadth int
	color	string
	
	geometry struct{
		area int
		perimeter int
	}
}

func main() {
	var rect rectangle 
	rect.length	=  10
	rect.breadth=  20
	rect.color	=  "Green"
	
	rect.geometry.area   =  rect.length * rect.breadth
	rect.geometry.perimeter	=  2 * (rect.length + rect.breadth)
	
	fmt.Println(rect)
	fmt.Println("Area:\t", rect.geometry.area)
	fmt.Println("Perimeter:", rect.geometry.perimeter)
}
golang struct

In the above example, selectors dot can be used as a rect.geometry.perimeter chain to fetch field values that are nested inside a struct.

2) var keyword and := operator

The following code snippet illustrates struct instantiating using var and := :

package main

import "fmt"

type rectangle struct {
	length  int
	breadth int
	color	string
}

func main() {
	var rect1 = rectangle{10,20,"Green"}
	fmt.Println(rect1)
	
	var rect2 = rectangle{length:10,color:"Green"} // breadth value skipped
	fmt.Println(rect2)
	
	rect3 := rectangle{10,20,"Green"}
	fmt.Println(rect3)
	
	rect4 := rectangle{length:10,breadth:20,color:"Green"}
	fmt.Println(rect4)	
		
	rect5 := rectangle{breadth:20,color:"Green"}	// length value skipped
	fmt.Println(rect5)
}
golang struct

You can skip value when you are specified fields name.

3) Using new keyword

The following code snippet illustrates struct instantiating using new keyword :

package main

import "fmt"

type rectangle struct {
	length  int
	breadth int
	color	string
}

func main() {
	rect1 := new(rectangle)
	rect1.length = 10
	rect1.breadth = 20
	rect1.color  = "Green"
	fmt.Println(rect1)
	
	var rect2 = new(rectangle)
	rect2.length = 10	
	rect2.color  = "Red"
	fmt.Println(rect2)	// breadth skipped
}
golang struct

rect2 is a pointer to an instance of rectangle

The fields are initially assign as a default of data type then we can reassign the field by using dot operator rect2.length.

4) Using &

The following code snippet illustrates struct instantiating using &:

package main

import "fmt"

type rectangle struct {
	length  int
	breadth int
	color	string
}

func main() {
	var rect1 = &rectangle{10,20,"Green"} // Can't skip any value
	fmt.Println(rect1)
	
	var rect2 = &rectangle{}
	rect2.length = 10	
	rect2.color  = "Red"
	fmt.Println(rect2)	// breadth skipped
	
	var rect3 = &rectangle{}
	(*rect3).breadth = 10
	(*rect3).color  = "Blue"
	fmt.Println(rect3)	// length skipped
}
golang struct

In the above example, when you instantiating like var rect1 = &rectangle{10,20,"Green"} you have to pass all field values and as per there data type otherwise compiler will give error like too few values in struct initializer or cannot use XXXXXX (type string) as type int in field value.

The fields are initially assign as a default of data type then we can reassign the field by using dot operator rect2.length.

Struct Exercise

1) Use field tags in the defination of Struct type

The following code snippet illustrates struct that id tagged with JSON annotation which can be interpreted by Go's JSON encoder and decoder:

package main

import (
    "fmt"
    "encoding/json"
)

type Employee struct {
    FirstName  string `json:"firstname"`
    LastName   string `json:"lastname"`
    City string `json:"city"`
}

func main() {
    json_string := `
	{
        "firstname": "Rocky",
        "lastname": "Sting",
		"city": "London"
    }`

    emp1 := new(Employee)
    json.Unmarshal([]byte(json_string), emp1)
    fmt.Println(emp1)

	emp2 := new(Employee)
	emp2.FirstName = "Ramesh"
	emp2.LastName = "Soni"
	emp2.City = "Mumbai"
    jsonStr, _ := json.Marshal(emp2)
    fmt.Printf("%s\n", jsonStr)
}
golang struct

In the above example, the tags are represented as raw string values which are wrapped within a pair of ``.

2) Nested Struc Type

Creating a Struct type using other struct types as the type for the fields of structs:

package main

import "fmt"

func main() {
	type Salary struct {
		Basic, HRA, TA float64
	}

	type Employee struct {
		FirstName, LastName, Email string
		Age                        int
		MonthlySalary              []Salary
	}

	e := Employee{
		FirstName: "Mark",
		LastName:  "Jones",
		Email:     "[email protected]",
		Age:       25,
		MonthlySalary: []Salary{
			Salary{
				Basic: 15000.00,
				HRA:   5000.00,
				TA:    2000.00,
			},
			Salary{
				Basic: 16000.00,
				HRA:   5000.00,
				TA:    2100.00,
			},
			Salary{
				Basic: 17000.00,
				HRA:   5000.00,
				TA:    2200.00,
			},
		},
	}
	fmt.Println(e.FirstName, e.LastName)
	fmt.Println(e.Age)
	fmt.Println(e.Email)
	fmt.Println(e.MonthlySalary[0])
	fmt.Println(e.MonthlySalary[1])
	fmt.Println(e.MonthlySalary[2])
}
golang struct

The Employee struct has been expanded by adding a new field MonthlySalary, for which the type is specified as a slice of a struct named Salary. Using the MonthlySalary field, you can specify monthly salary for an employee.

3) Adding Methods to Struct Types

The following code snippet illustrates an example of Struc Type with Method:

package main

import (
	"fmt"
)

type Salary struct {
	Basic, HRA, TA float64
}

type Employee struct {
	FirstName, LastName, Email string
	Age                        int
	MonthlySalary              []Salary
}

func (e Employee) EmpInfo() string {
	fmt.Println(e.FirstName, e.LastName)
	fmt.Println(e.Age)
	fmt.Println(e.Email)
	for _, info := range e.MonthlySalary {
		fmt.Println("===================")
		fmt.Println(info.Basic)
		fmt.Println(info.HRA)
		fmt.Println(info.TA)
	}
	return "----------------------"
}
func main() {

	e := Employee{
		FirstName: "Mark",
		LastName:  "Jones",
		Email:     "[email protected]",
		Age:       25,
		MonthlySalary: []Salary{
			Salary{
				Basic: 15000.00,
				HRA:   5000.00,
				TA:    2000.00,
			},
			Salary{
				Basic: 16000.00,
				HRA:   5000.00,
				TA:    2100.00,
			},
			Salary{
				Basic: 17000.00,
				HRA:   5000.00,
				TA:    2200.00,
			},
		},
	}
	fmt.Println(e.EmpInfo())
}
golang struct

Go language type system allows you to add methods to struct types using a method receiver. The method receiver specifies which type has to associate a function as a method to that type.