Structs in GoLang

Structs are a way to structure and use data. It allows us to group data. In this article, we will see how to declare and use it.

Defining a Struct in Go

To use a struct we declare the type of struct we are going to use. The code below shows how to define a struct type using the type keyword.

package main

import (
	"fmt"
)

type Fruit struct {
	name string
}

// to define a struct type use
// type structname struct {
//        field1 type1
//        field2 type2
//        ...
// }

func main() {
}

Declaring Struct Variables

We will see how to declare a struct variable. It is really simple as shown below.

var variablename structname    // declare a struct variable

What is a zero-value Struct?

When a struct is declared but has not assigned any value, it is then a zero-valued struct. That means, all the fields will take their respective zero-values from their types.

package main

import (
	"fmt"
)

type Fruit struct {
	name string
}

func main() {
	var apple Fruit
	fmt.Println(apple)      // prints {}
}

Creating and initializing a Struct in GoLang

Now, we will create structs and initialize them with values. There are a few ways we could do that.

1. Using struct Literal Syntax

Struct literal syntax is just assigning values when declaring and it is really easy.

package main

import (
	"fmt"
)

type Fruit struct {
	name string
}

func main() {
	var apple = Fruit{"Apple"} // struct literal syntax
	fmt.Println(apple)      // prints {Apple}
}

2. Using the new keyword

We can use the new keyword when declaring a struct. Then we can assign values using dot notation to initialize it.

package main

import (
	"fmt"
)

type Fruit struct {
	name string
}

func main() {
	var banana = new(Fruit)
	banana.name = "Banana"
	fmt.Println(banana)      // prints &{Banana}
}

3. Using pointer address operator

The pointer address operator(&) can be used to declare a struct. Let’s see how we would do that.

package main

import (
	"fmt"
)

type Fruit struct {
	name string
}

func main() {
	var mango = &Fruit{"Mango"}
	fmt.Println(mango)      // prints &{Mango}
}

Types of Structs in Go

A struct can both be named as well as unnamed or anonymous. Both have their uses. We will what are those and how to use them.

1. Named struct

A named struct is any struct whose name has been declared before. So, it can be initialized using its name.

type Food struct {}  // Food is the name

2. Anonymous struct

Now we will see the anonymous structs. They come in very handy. We will see how we create and use them.

package main

import (
	"fmt"
)

func main() {
	
	pizza := struct {
		name string
	}{
		name: "Pizza",
	}
	
	fmt.Println(pizza)      // prints {Pizza}
}

Fields of a Struct Object

Structs have fields. It can be named or unnamed. They can be assigned values or can remain empty and thus have zero-value. Let’s see how to initialize and use them.

1. Named fields in a struct

Named fields are those whose name has been declared in the struct type declaration. Below is an example.

type Food struct {
        name string         // this field has name
        quantity int        // ...
} 

2. Anonymous fields in a struct

Anonymous fields are those whose type is declared only. In the example, we can see that any type can be used inside the struct. But this can create ambiguity and may result in a problem as you will see in the code.

package main

import (
	"fmt"
)

// none of the fields are named
// these are anonymous fields

// Throws error because of ambiguity
// There are two string fields to choose from
// p.string makes no sense to the compiler

// type Painting struct {
// 	string           // painting's name
//	string           // artist's name
// }

type Painting struct {
	string           // painting name
	Artist           // artist
}

type Artist struct {
	string
}

func main() {
	var p Painting
	p.string = "Starry Night"
	// p.string = "Van Gogh"       // it will throw an error
	                               // "ambiguous selector p.string"
	
	p.Artist.string = "Van Gogh"
	fmt.Println(p)      // prints {Starry Night {Van Gogh}}
}

3. Functions as a field

Structs can have functions as their field. Below is the code showing the usage of function inside a struct.

import (
	"fmt"
)

// declare function type
type FoodNameGetter func(string) string

type Food struct {
	name string
	getter FoodNameGetter // declare function
}


func main() {
	pizza := Food{
		name: "Pizza",
		getter: func(name string) string {         // declare function body
			return "This is " + name + "."
		},
	}
	
	fmt.Println(pizza.getter(pizza.name))     // prints "This is Pizza."
}

Accessing fields of a Struct

Accessing fields of the struct are really simple. We use the dot operator for that. When a nested structure is there we use dot inside the nested struct as well.

// declaration
type Car struct {
        name string
        cost int
}

var c Car = Car{"Mercedes", 3500000}
// accessing
carMake := c.name
carPrice := c.cost

Now, for a nested struct, we can do like this:

package main

import (
	"fmt"
)

type User struct {
	name string
}

type Service struct {
        name string
	user User
}

func main() {

	google := Service{
		name: "Google",
		user: User{
			name: "John Doe",
		},
	}

	// accessing from nested struct
	fmt.Println(google.user.name)  // prints "John Doe"
}

When we use an anonymous struct inside another struct, all the fields from the anonymous struct becomes accessible from the outer struct as if it is a part of it. These fields are called promoted fields.

package main

import (
	"fmt"
)

type Person struct {
	name string
}

type StoreKeeper struct {
	Person
}

func main() {
	var p = StoreKeeper{}
	p.Person = Person{"Jane Doe"}
	
	// access the field as normal field
	fmt.Println(p.name)                   // prints "Jane Doe"
}

Using pointers of struct

We can create pointers of a struct using the address-of operator(&). Here is an example showing just that.

package main

import (
	"fmt"
)

type Student struct {
	name string
}

func main() {
	ptrStudent := &Student{name: "John"}
	
	fmt.Println(ptrStudent) // prints &{John}
}

We can use new() function to get a pointer of that struct.

Comparing Structs

The structs can be compared if they have the same values in the same fields. Here is an example.

package main

import (
	"fmt"
)

type Student struct {
	name string
}

func main() {
	s1 := Student{"John"}
	s2 := Student{"John"}
	
	if(s1 == s2) {
		fmt.Println("They are equal")
	} else {
		fmt.Println("They are not equal")
	}
	
	// output: "They are equal"
}

Array of Structs

Now we will see how to use structs in arrays. It is pretty simple and straightforward.

1. Declaring and initializing an Array of Structs

We simply declare an array with the type of that struct.

var arrayOfStruct []StructName     // declaration

2. Inserting values in an Array of Structs

Use append function which returns the slice after insertion.

package main

import (
	"fmt"
)

type Student struct {
	name string
}

func main() {
	// declare array
	var students []Student
	
	s := Student{"Kate"}
	kate := append(students, s)
	fmt.Println(kate)             // [{Kate}]
}

Or, we can simply use indexing like this.

students[1] = Student{"Peter"}

This is how we can use structs in Go for various purposes.