Advanced GoLang Concepts: Channels, Context, and Interfaces. Part 2

Rebeccah Wambui
4 min readOct 3, 2023

--

Photo by Mustafa Omar on Unsplash

Introduction

If you are reading this, then you’ve probably read part one, on Golang Channels, how channels are used to achieve concurrency in Go, different ways to implement channels as well as some real-life examples. If not, you can find the first part of this short series here.

This article is a continuation of the Advanced GoLang concepts, and we’ll be covering contexts and Interfaces. As usual, It will be pretty cool if you open your IDE, and follow the steps, as we learn together!

Context

A context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.

Let’s look at an example, say we have this simple program that gets users from an API and returns the size of the data fetched.

func main() {

// create http request
req, err := http.NewRequest(http.MethodGet, "https://jsonplaceholder.typicode.com/users", nil)
if err != nil {
fmt.Printf("error fetching users")
}

//perform the http request
res, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("error processing request")
}

defer res.Body.Close()

//get body from the response
data, err := io.ReadAll(res.Body)
if err != nil {
fmt.Printf("error reading response body")
}

fmt.Printf("data of size %d\n", len(data))
}

Response:

% go run .
data of size 5645

Assuming we want to cancel the request if it takes longer than a certain amount of time, we’ll use context to cancel this request.

timeoutContext, cancel := context.WithTimeout(context.Background(), time.Millisecond * 100)
defer cancel()

req, err := http.NewRequestWithContext(timeoutContext, http.MethodGet, "https://jsonplaceholder.typicode.com/users", nil)

From this code;

We’re declaring the timeoutContext and pass in a context.WithTimeout. WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). Canceling this context releases resources associated with it, so the code should call cancel as soon as the operations running in this Context are complete. In the request, we pass http.NewRequestWithContext and the timeContext. That way, anytime the request exeeds 100 milliseconds before it gets the users, it’s cancelled

This helps to make your programs more efficient so that they are not doing more work than they need to.

Interfaces

The key principle of an interface in Go is to provide method signatures for similar types of objects. Go as a programming language doesn’t have classes and inheritance to create and implement object orientation, it uses abstract types and interfaces to achieve Polymorphism.

How to create interfaces

An interface is created using the type keyword followed by the name of the interface and then the interface keyword. You can specify the method signatures inside the curly braces.

Here’s an example;

package main

type Employee interface {
// method signatures
GetName() string
}

To implement an interface, you need to implement all the methods declared in the interface. It’s important to note that Interfaces in Go are implemented implicitly

Here’s an example of how interfaces are implemented.

package main

import "fmt"


// declare an interface employee
type Employee interface {
GetName() string
}

// create a struct
type Engineer struct {
Name string
}

type Finance struct {
Name string
}

// create a method that implements the Employees's interface
func (e *Engineer) GetName() string {
return "employee name:" + e.Name

}

// create a method that implements the Employee's interface
func (f Finance) GetName() string {
return "employee name:" + f.Name
}

// print out the engineer's details
func EmployeeDetails(e Employee) {
fmt.Println(e.GetName())
}

func main() {
engineer := &Engineer{
Name: "Rebeccah",
}
EmployeeDetails(engineer)

finance := &Finance{
Name: "John Doe",
}
EmployeeDetails(finance)
}

Output:

go run main.go
employee name: Rebeccah
employee name: John Doe

Empty Interfaces

An empty interface is a type of interface that has zero methods. Normally in functions, when we pass values to the function parameters, we need to specify the data type of parameters in a function definition.

However, with an empty interface, we can pass parameters of any data type. For example,

var i interface{}

func getType(i interface{}){
fmt.Printf("Type = %T, value = %v\n", i, i)
}

func main() {
d:= "Golang concepts"
getType(d)

x:= 5
getType(x)
}

Output:

go run main.go
Type = string, value = Golang concepts
Type = int, value = 5

Here variable i is of empty interface type.Function getType takes in the i intaface and prints out the type and value. In our func main, we have var d which we pass in a string Golang concepts then call the getType method.

GoLang Type Assertions

Now that we’ve seen how we can use empty interfaces, we know that an empty interface can accept any type and number of values. This may often create ambiguity on what data an interface is holding. To remove this, we use type assertions.

Example:

func main() {
var x interface{}

// store a string to an empty interface
x = "Type assertion"

// type assertion
value := x.(string)
fmt.Println(value)

}

Output:

go run main.go
Type assertion

Here, we have this line of code that explicitly states that the data we’re passing is a string

Why are Interfaces important?

  • Modularity − They help to make code more modular and easier to maintain by separating the interface from the implementation details.
  • Reusability − Interfaces allow you to write code that can work with any type that satisfies that interface, which means that you can reuse the same code for multiple types.
  • Testability − They make it easier to write testable code by allowing you to create mock implementations of the interface for testing purposes.

Conclusion

In this article, we’ve discussed how to achieve polymorphism using interfaces in Go. An interface is a collection of method signatures that any type can implement, allowing us to write code that can work with any type that implements an interface, without knowing the underlying type.

References

I hope you’ve learnt something new/awesome. Feel free to share your feedback, questions, or comments 🤓

If you liked this article click 👏 below!

Happy learning🤓

--

--

Rebeccah Wambui

Software Engineer @Twigafoods || Problem Solver || Happy to share and learn with and from You all 🌟