Skip to main content

Structs and Interfaces in Go

Hey there! In this guide, we'll explore Structs and Interfaces in Go. Go is not a traditional object-oriented language—it doesn't have classes or inheritance. Instead, it uses structs and interfaces to achieve similar, and often more flexible, designs. Let's dive in!

1. Structs in Go

A struct (short for structure) is a typed collection of fields. They are useful for grouping data together to form records.

Defining a Struct

You define a struct using the type and struct keywords.

type Person struct {
FirstName string
LastName string
Age int
}

Initializing a Struct

You can create an instance of a struct by providing values for its fields.

// Creating a struct with field names (recommended)
p1 := Person{
FirstName: "Alice",
LastName: "Smith",
Age: 30,
}

// Creating a struct without field names (relies on field order)
p2 := Person{"Bob", "Jones", 25}

Accessing Struct Fields

You can access or modify the fields of a struct using the dot . operator.

fmt.Println(p1.FirstName) // Output: Alice
p1.Age = 31 // Update age

2. Methods in Go

Go does not have classes. However, you can define methods on struct types! A method is simply a function with a special receiver argument.

// Method with a receiver of type Person
func (p Person) FullName() string {
return p.FirstName + " " + p.LastName
}

func main() {
p := Person{FirstName: "Alice", LastName: "Smith"}
fmt.Println(p.FullName()) // Output: Alice Smith
}

Pointer Receivers

If you want to modify the struct within the method, you must use a pointer receiver (*Person). If you just use a value receiver (like above), Go passes a copy of the struct.

func (p *Person) HaveBirthday() {
p.Age++ // This actually modifies the original struct!
}

3. Interfaces in Go

Interfaces in Go provide a way to specify the behavior of an object: if something can do this, then it can be used here.

An interface type is defined as a set of method signatures.

Defining an Interface

type Speaker interface {
Speak() string
}

Implementing an Interface

In Go, interfaces are implemented implicitly. You don't use an implements keyword. If a type has all the methods defined by an interface, it automatically implements that interface!

type Dog struct{}

func (d Dog) Speak() string {
return "Woof!"
}

type Cat struct{}

func (c Cat) Speak() string {
return "Meow!"
}

Because both Dog and Cat have a Speak() string method, they both implement the Speaker interface.

Using Interfaces

You can use interfaces to write functions that accept any type that implements the interface.

func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}

func main() {
myDog := Dog{}
myCat := Cat{}

MakeSound(myDog) // Output: Woof!
MakeSound(myCat) // Output: Meow!
}

4. The Empty Interface

The interface type that specifies zero methods is known as the empty interface: interface{} (or any in modern Go). Because every type has at least zero methods, every type implements the empty interface. It is used to handle values of unknown type.

func PrintAnything(v any) {
fmt.Println(v)
}

5. Best Practices

  • Favour small interfaces: In Go, interfaces usually have just one or two methods. (e.g., io.Reader, io.Writer).
  • Accept interfaces, return structs: It is a common Go idiom to have functions take interfaces as arguments (for flexibility) but return concrete struct types.