Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
239: Empty Interface Usage and any
I see this mistake constantly when developers coming from Python or TypeScript start with Go: they treat any (or interface{}) as a way to "turn off" the type system. They think that by marking a variable as any, they've created a dynamic object that they can just call methods on, hoping the right method exists at runtime.
Let's look at why that's a dangerous assumption. Suppose you have a User struct and a Company struct, both of which have a Describe() method. You might try to do something like this:
type User struct{ Name string }
func (u User) Describe() string { return "User: " + u.Name }
type Company struct{ Name string }
func (c Company) Describe() string { return "Company: " + c.Name }
func PrintDetails(item any) {
// This will NOT compile
fmt.Println(item.Describe())
}
The compiler will scream at you. Why? Because any doesn't mean "this object might have a Describe method." It means "this variable can hold any type, but right now, the compiler knows absolutely nothing about it." You can't call Describe() because any itself doesn't have a Describe() method.
Thinking 'any' removes the need for type checking vs. realizing it defers it to runtime
In Go, any is just an alias for interface{}. An interface is essentially a contract. An empty interface is a contract with zero requirements. Since every single type in Go (ints, strings, custom structs) satisfies "zero requirements," every type can be stored in an any variable.
But here is the catch: while you can put anything into an empty interface, you can't get anything out of it without proving to the compiler what it is. I usually tell my juniors to think of any as a sealed box. You know there's something inside, but you can't interact with it until you open the box and verify the contents.
To actually use the value, you have two main options: a type assertion or a type switch. If you're only expecting one specific type, a type assertion is the way to go:
func ProcessValue(val any) {
s, ok := val.(string)
if !ok {
fmt.Println("Expected a string, but got something else")
return
}
fmt.Println("The string length is:", len(s))
}
Assuming 'any' is a magic container vs. using Type Switches to safely unpack data
When you're dealing with a variable that could truly be one of several different types—like a generic event handler or a configuration parser—don't lean on multiple if statements with assertions. That's clunky. Instead, use a type switch. It's cleaner, more idiomatic, and much easier for the next person reading your code to follow.
I use this pattern all the time when writing middleware that needs to handle different types of metadata. Here is a concrete example of how to handle a "Value" that could be a few different things:
func FormatConfigValue(val any) string {
switch v := val.(type) {
case int:
return fmt.Sprintf("Integer value: %d", v)
case string:
return fmt.Sprintf("String value: %s", v)
case bool:
return fmt.Sprintf("Boolean value: %t", v)
case nil:
return "Value is nil"
default:
return fmt.Sprintf("Unknown type: %T", v)
}
}
Notice how the variable v inside each case is automatically cast to the correct type. In the int case, v is an int, not an any. This is where the magic happens—you've safely moved from the "unknown" world of any back into the "safe" world of strong typing.
One last piece of advice: avoid using any whenever possible. If you know your function should only accept things that can be "Stringified," define an interface with a String() string method. Only reach for any when you truly have no idea what the input will be, such as when decoding JSON into a map or handling arbitrary data from an external API.
📋 Practical Task
Building a Polymorphic Notification Dispatcher
You are building a notification system that can send messages via different channels. Depending on the payload passed to the dispatcher, it should behave differently.
Requirements:
- Define two structs:
EmailPayload(with aSubjectandBodystring) andSMSPayload(with aPhoneNumberandMessagestring). - Create a function called
DispatchNotification(payload any). - Inside
DispatchNotification, use a type switch to:- If it's an
EmailPayload, print:"Sending Email: [Subject] - [Body]". - If it's an
SMSPayload, print:"Sending SMS to [PhoneNumber]: [Message]". - For any other type, print:
"Unsupported notification type".
- If it's an
- In your
mainfunction, callDispatchNotificationthree times: once with an email, once with an SMS, and once with a plain integer to test the default case.
There are no comments for now.