Understanding Interfaces in Go

As someone who's worked and learned various programming languages, I’ve encountered interfaces in many forms. When I finally started using Go, I was intrigued by how its approach to interfaces could streamline my coding experience. Let’s break down what they are, how they work and explore a practical example using map[string]interface{}.

What’s an Interface?

At its core, an interface in Go defines a set of methods that a type must implement. The great part? There’s no need for a type to declare that it implements an interface. If it has the right methods, it’s in. This implicit satisfaction keeps the code cleaner and promotes better organization—something I appreciated after dealing with the more verbose requirements in languages like Java or C#.

How Go's Interfaces Differ from Others:

In many languages, interfaces require explicit declaration. For example, in Java or C#, a class must state that it implements an interface. Go takes a different approach: if a type has the methods, it automatically satisfies the interface. This means less boilerplate code and more flexibility, as you can easily add new types without modifying existing ones.

This design encourages a focus on ‘behaviour’ over “type hierarchy”, which I found refreshing. It’s a shift that can lead to more maintainable code.

The interface{} type:

The empty interface, interface{}, can hold any type. This is useful, but it also means you might need to do type assertions later, which can add some complexity. It’s a trade-off for the added flexibility.

A Simple Example: Using map[string]interface{}

Let’s look at a very basic scenario: managing patient data via Struct. We’ll define a Patient struct and convert it to a map[string]interface{} using JSON marshaling.

Here’s how it looks:

package main

import (
    "encoding/json"
    "fmt"
)

type Patient struct {
    FirstName  string
    SecondName string
    Age        int
    PAllergy   bool
}

func main() {
    p1 := Patient{
        FirstName:  "Ashwin",
        SecondName: "Gopalsamy",
        Age:        24,
        PAllergy:   false,
    }

    // Convert to JSON & then to a map
    jsonData, _ := json.Marshal(p1)
    var result map[string]interface{}
    json.Unmarshal(jsonData, &result)

    for key, value := range result {
        fmt.Printf("%s: %v\n", key, value)
    }
}

Whats Happening Here?

  1. Defining the Struct: We start with a simple Patient struct.

  2. JSON Marshaling: json.Marshal converts the struct into JSON.

  3. Unmarshaling into a Map: We then turn that JSON into a map[string]interface{}. This lets us handle different types dynamically.

  4. Looping Through the Map: Finally, we print each key-value pair. This shows how interface{} can work with various types.

Why Use Interfaces?

Interfaces make it easy for your functions to accept different types. When dealing with user input or APIs, using map[string]interface{} can simplify handling dynamic data. They also help keep your code modular and easier to test, as demonstrated in our example. You’re not tied to specific types, allowing you to focus on implementing behavior rather than declaring it.


I intend to simplify the fundamentals of Go with this series of blogs as I read and deepen my understanding. The more you teach, the more you learn and grow.

May the code be with you. <3

0
Subscribe to my newsletter

Read articles from Ashwin Gopalsamy directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ashwin Gopalsamy
Ashwin Gopalsamy

Product-first engineer, blogger and open-source contributor with around 4 years of experience in software development, cloud-native architecture and distributed systems. I build fintech products that process millions of transactions daily and drive substantial revenue. My expertise spans designing, architecting and deploying scalable software, focusing on the business under the code. I collaborate closely with engineers, product owners, and guilds, known for my clear communication and team-centric approach in dynamic environments. Colleagues appreciate my adaptability, openness and focus on diverse, meaningful contributions. Beyond coding, I’m recognized for my documentation, ownership and presentation skills, which drive clarity and engagement across teams. Bilingual in English and Deustch, I bridge cross-functional teams across geographies, ensuring smooth, efficient communication. I’m always open to new opportunities for connection and collaboration. Let’s connect and explore ways to create together.