Question
In Go, a value of type T can be assigned to an interface{} value when T implements that interface. However, a slice like []T cannot be passed directly to a function that expects []interface{}.
For example:
package main
func foo(items []interface{}) {
// do something
}
func main() {
a := []string{"hello", "world"}
foo(a)
}
This produces the error:
cannot use a (type []string) as type []interface{} in function argument
Even an explicit conversion does not work:
b := []interface{}(a)
That also fails with:
cannot convert a (type []string) to type []interface{}
To work around this, it is common to create a new slice and copy each element:
b := make([]interface{}, len(a))
for i := range a {
b[i] = a[i]
}
Why does Go behave this way? Is there a non-trivial reason that []T is not convertible to []interface{}? Also, is there a better or more standard way to handle this kind of conversion?
Short Answer
By the end of this page, you will understand why Go allows T to become interface{} but does not allow []T to become []interface{}. You will learn the memory-layout reason behind this rule, how to perform the conversion correctly, when a manual loop is necessary, and how real Go code usually avoids this problem through better API design.
Concept
In Go, an individual value and a slice of values are very different things.
A value like string can be stored inside an interface{} because an interface value is designed to hold:
- the concrete type information
- the concrete value
So this is valid:
var s string = "hello"
var x interface{} = s
But a slice is not "a bag of individually converted values." A slice is a small descriptor pointing to an underlying array. The element type of that array matters.
[]stringpoints to an array ofstringvalues[]interface{}points to an array ofinterface{}values
Those are different underlying array layouts.
That means Go cannot safely reinterpret one as the other.
Why this matters
If Go allowed []string to be used as []interface{} directly, code inside the function could try to store a non-string value into that slice:
Mental Model
Think of a slice as a tray with slots of a specific shape.
- A
[]stringis a tray where every slot is shaped forstring - A
[]interface{}is a tray where every slot is shaped forinterface{}
Even though you can place one string into one interface{} box, that does not mean a whole tray of string-shaped slots can suddenly become a tray of interface-shaped slots.
To make that happen, you must build a new tray and place each item into its new slot one by one.
That is why the loop is necessary.
Syntax and Examples
Core syntax
Converting individual values to interface{}:
var s string = "hello"
var v interface{} = s
Converting a slice requires manual copying:
strings := []string{"hello", "world"}
values := make([]interface{}, len(strings))
for i, s := range strings {
values[i] = s
}
Example: calling a function that expects []interface{}
package main
import "fmt"
func printAll(items []interface{}) {
for _, item := range items {
fmt.Println(item)
}
}
func main() {
words := []string{"Go", "is", "simple"}
items := ([]{}, (words))
i, w := words {
items[i] = w
}
printAll(items)
}
Step by Step Execution
Consider this example:
package main
import "fmt"
func main() {
words := []string{"hello", "world"}
items := make([]interface{}, len(words))
for i, w := range words {
items[i] = w
}
fmt.Println(items)
}
Step-by-step
1. Create the original slice
words := []string{"hello", "world"}
Now words is a slice whose underlying array stores two string values.
2. Create a new destination slice
items := make([]interface{}, len(words))
Now items is a different slice with a different underlying array. Its elements are interface{} values.
3. Loop through the original slice
Real World Use Cases
1. Logging and formatted output
Some APIs accept values as interface{} so they can handle mixed types. If your data starts as []string or []int, you may need to convert it before passing it to a generic formatter.
2. Generic containers in older Go code
Before generics, many reusable utilities used interface{} to accept any type. Converting slices into []interface{} was a common pattern.
3. Database or serialization helpers
Some helpers expect []interface{} for parameter lists, especially when building query arguments dynamically.
Example:
args := []interface{}{id, name, active}
If those values originally came from typed slices, they must be copied in.
4. Reflection-based libraries
Libraries that use reflection often expose APIs based on interface{}. Typed slices cannot be passed directly unless the library accepts any and handles the slice internally.
5. Variadic APIs
Functions with ...interface{} are common in Go, such as formatting and logging functions. Passing a typed slice to them often requires conversion first.
Real Codebase Usage
In real Go projects, developers usually try to avoid requiring []interface{} unless it is truly necessary.
Common patterns
1. Prefer typed parameters
Instead of writing:
func foo(items []interface{})
prefer:
func foo(items []string)
if the function really only works with strings.
This is clearer, safer, and avoids conversions.
2. Use variadic parameters when appropriate
For APIs meant to accept arbitrary values:
func logValues(values ...interface{})
Callers can pass mixed values directly:
logValues("user", 42, true)
But a typed slice like []string still must be converted if expanded.
Common Mistakes
Mistake 1: Assuming element conversion implies slice conversion
Broken code:
words := []string{"a", "b"}
var items []interface{} = words
Why it fails:
stringcan becomeinterface{}[]stringcannot become[]interface{}automatically
Fix:
items := make([]interface{}, len(words))
for i, w := range words {
items[i] = w
}
Mistake 2: Trying an explicit cast
Broken code:
items := []interface{}(words)
Why it fails:
Go does not support this conversion because the slice element types have different representations.
Mistake 3: Forgetting that ...interface{} has the same issue
Broken code:
Comparisons
| Concept | What it means | Allowed automatically? | Why |
|---|---|---|---|
T -> interface{} | One concrete value stored in an interface | Yes | Interface values are designed for this |
[]T -> []interface{} | A whole typed slice treated as interface slice | No | Underlying element layouts differ |
[]T -> []any | Same as above (any is alias for interface{}) | No | Same rule |
[]string -> []byte |
Cheat Sheet
Quick rules
Tcan be assigned tointerface{}ifTimplements the interface.[]Tcannot be assigned or converted to[]interface{}directly.anyis just an alias forinterface{}.- Converting
[]Tto[]interface{}requires a new slice and a loop.
Standard conversion pattern
result := make([]interface{}, len(items))
for i, v := range items {
result[i] = v
}
Generic helper
func toInterfaces[T any](items []T) []interface{} {
result := make([]interface{}, len(items))
for i, v := range items {
result[i] = v
}
result
}
FAQ
Why can Go convert string to interface{} but not []string to []interface{}?
Because a single value conversion is different from reinterpreting an entire slice. The two slice types use different element representations in memory.
Is []any different from []interface{}?
No. any is just an alias for interface{} in Go. []any and []interface{} are the same type.
Is there a built-in function to convert []T to []interface{}?
No general built-in conversion exists. The usual approach is to allocate a new slice and copy elements in a loop.
Is the manual conversion expensive?
It has a cost: a new slice allocation plus per-element assignment. For small slices this is usually fine, but it can matter in performance-sensitive code.
Can generics solve this problem?
Often, yes. Instead of accepting []interface{}, a generic function can accept []T, which avoids the conversion entirely.
Mini Project
Description
Build a small utility that prints slices of different types in a generic-looking way. This project demonstrates the practical issue behind converting []T to []interface{} and shows both the classic conversion approach and a better generic approach.
Goal
Create a Go program that can print string and integer slices, first by converting them to []interface{}, and then by using a generic helper that avoids the conversion problem.
Requirements
- Create a function that accepts
[]interface{}and prints each item. - Write code to convert a
[]stringinto[]interface{}before calling that function. - Write code to convert a
[]intinto[]interface{}before calling that function. - Add a generic function that prints any slice without converting it to
[]interface{}. - Demonstrate both approaches in
main.
Keep learning
Related questions
Blank Identifier Imports in Go: What `_` Means in an Import Statement
Learn what `_` means in a Go import, why blank identifier imports run package init code, and when to use them safely.
Check if a Value Exists in a Slice in Go
Learn how to check whether a value exists in a slice in Go, and why Go has no Python-style `in` operator for arrays or slices.
Concatenating Slices in Go with append
Learn how to concatenate two slices in Go using append and the ... operator, with examples, pitfalls, and practical usage.