Introduction: why "type converting slices of interfaces" is a pain for every Go developer
\n\nIf you've ever written Go code that accepts []interface{} and tried to convert it to []string or []int, you've likely encountered this annoying compiler message: cannot convert []interface{} to []string. This is one of the most popular problems on Stack Overflow, with thousands of views. And it's no surprise — at first glance, logic suggests: "If each element is an interface{} that stores a string, why can't I just convert the entire slice?"
The answer lies in how Go works with types and memory. Type converting slices of interfaces is not just type casting, but a full data copy. In this article, we'll break down the problem from top to bottom: starting with theory, going through practical examples, and ending with ready-made solutions you can immediately use in your projects.
\n\nBy the way, if you write in Go and work with undefined data types — for example, when parsing JSON or processing data from external sources — this topic is critically important for you. Let's go!
\n\nWhy doesn't Go allow directly converting []interface{} to []T?
\n\nLet's start with a fundamental understanding. In Go, interface{} is a special type that stores a pair: (type, value). When you have []interface{}, each element occupies a different number of bytes in memory (depending on the stored type). And []string is a contiguous array of string structures (each string is a pointer to data + length).
Imagine you have a box with multi-colored balls of different sizes (that's []interface{}), and you need to move them into a box where all balls are the same color and size ([]string). You can't just move the box — you need to check each ball, take it out, and put it into a new box. That's exactly what happens during type converting slices of interfaces.
Key point: type assertion only works with individual elements, not with the entire slice as a whole. Go does not provide syntactic sugar for mass conversion — this is done intentionally so that you explicitly control what happens to each element.
\n\nExample of an error you've likely seen
\n\npackage main\n\nimport "fmt"\n\nfunc main() {\n data := []interface{}{"apple", "banana", "cherry"}\n // Compilation error:\n // cannot use data (variable of type []interface{}) as type []string in assignment\n // var fruits []string = data\n fmt.Println(data)\n}\n\n\nThis code will not compile. The Go compiler is strict about types, and implicit conversion of slices of different types is prohibited. Even if you are sure that only strings are inside.
\n\nThe correct way: Type Assertion for each element
\n\nThe simplest and most understandable way to perform type converting slices of interfaces is to iterate over each element in a loop and apply type assertion.
\n\nBasic example with type assertion
\n\npackage main\n\nimport "fmt"\n\nfunc convertToStrings(data []interface{}) []string {\n result := make([]string, 0, len(data))\n for _, v := range data {\n // Attempt to assert that v is a string\n if str, ok := v.(string); ok {\n result = append(result, str)\n } else {\n // Error handling: element is not a string\n fmt.Printf("Warning: element %v is not a string, skipping\\n", v)\n }\n }\n return result\n}\n\nfunc main() {\n mixed := []interface{}{"hello", 42, "world", true, "golang"}\n strings := convertToStrings(mixed)\n fmt.Println(strings) /