Question
I am new to Go interfaces and am working with SOAP requests. What does interface{} mean in this struct?
type Envelope struct {
Body `xml:"soap:"`
}
type Body struct {
Msg interface{}
}
I have also seen similar syntax in functions such as fmt.Println. What does an empty interface, interface{}, achieve in Go?
Short Answer
interface{} is Go's empty interface. A value of this type can hold a value of any concrete type because every Go type implements an interface with zero required methods. You will learn how to assign values to it, retrieve their original types safely, and recognize when a more specific type is a better design.
Concept
In Go, an interface describes behavior through a set of methods. For example:
type Stringer interface {
String() string
}
Only types with a String() string method satisfy Stringer.
An empty interface has no required methods:
interface{}
Because there are no methods to implement, every Go type satisfies interface{}. It can store an int, string, struct, slice, pointer, function, or another interface.
var value interface{}
value = 42
value = "hello"
value = []string{"red", "blue"}
At runtime, an interface value conceptually carries two pieces of information:
- The concrete type currently stored, such as
intorstring. - The concrete value itself.
In your SOAP-related structure:
type Body struct {
Msg interface{}
}
Msg can hold different message structs. This can be useful when a SOAP body may contain one of several possible XML payload types. However, the code that reads Msg must determine which concrete type was stored.
In modern Go, any is an alias for interface{}:
var value any = "hello"
any and interface{} mean exactly the same thing. Many new Go codebases prefer any because it is easier to read.
Mental Model
Think of a normal variable as a labelled box that accepts one kind of item:
var age int
The age box accepts integers only.
An interface{} variable is a flexible box with no restriction on what kind of Go value it can hold:
var item interface{}
You can put an integer in it now and a string later. But when you take something out, you need to check what it is before treating it as a particular type.
The box remembers the item's type, but the variable's declared type is still interface{}. Go therefore does not let you use it as an int, string, or custom struct until you inspect or assert its concrete type.
Syntax and Examples
Declare an empty-interface variable with either interface{} or any:
var first interface{}
var second any
Assign different concrete values:
package main
import "fmt"
func main() {
var value interface{}
value = 25
fmt.Println(value)
value = "Go"
fmt.Println(value)
value = true
fmt.Println(value)
}
fmt.Println can print each value because it accepts values of any type. Its simplified signature is:
func Println(a ...any) (n int, err error)
The ... means it accepts zero or more arguments, and any means each argument may have any concrete type:
fmt.Println("status", 200, true)
Reading a concrete value with a type assertion
Use a type assertion when you expect a specific type:
var value any = "SOAP request"
text, ok := value.(string)
if ok {
fmt.Println("Message:", text)
}
The ok result is true only when value currently contains a string. This form is safe because it does not panic when the type differs.
Storing a custom message
type LoginRequest struct {
Username string
}
body := Body{
Msg: LoginRequest{Username: "ada"},
}
request, ok := body.Msg.(LoginRequest)
if ok {
fmt.Println(request.Username)
}
Step by Step Execution
Consider this program:
package main
import "fmt"
func main() {
var message interface{} = 200
number, ok := message.(int)
fmt.Println(number, ok)
text, ok := message.(string)
fmt.Println(text, ok)
}
Step by step:
messageis declared asinterface{}and stores the concrete value200.- Its current concrete type is
int. message.(int)succeeds, sonumberbecomes200andokbecomestrue.- The first
Printlnoutputs200 true. message.(string)does not match because the stored value is anint, not astring.- The safe assertion returns the zero value for
string("") andfalse. - The second
Printlnoutputsfalse.
A direct assertion without ok panics if it is wrong:
text := message.(string) // panic: interface conversion: interface {} is int, not string
Use the two-result form unless a mismatch is truly impossible.
Real World Use Cases
Common uses of interface{} or any include:
- Formatting and logging: Functions such as
fmt.Println,fmt.Printf, and many logging libraries accept values of many types. - Decoded JSON: When decoding JSON into an unknown shape, a program may use
map[string]anyorany. - Generic event payloads: An event system may carry different payload structs for different event names.
- Plugin or extension points: A framework may receive values whose concrete types are chosen by callers.
- SOAP/XML message containers: A body that can hold several possible request or response structs may temporarily use an interface field.
For a SOAP message, a more concrete design is often easier when the body format is known:
type Envelope struct {
Body Body `xml:"soap:Body"`
}
type Body struct {
Msg LoginRequest `xml:"LoginRequest"`
}
Use any only when Msg genuinely needs to hold multiple possible types.
Real Codebase Usage
In production Go code, empty interfaces are used carefully. They are useful at boundaries where the shape of data is unknown, but specific types are preferred inside business logic.
Validate unknown input with a type switch
A type switch handles several possible concrete types:
func describe(value any) string {
switch v := value.(type) {
case string:
return "text: " + v
case int:
return fmt.Sprintf("number: %d", v)
case nil:
return "no value"
default:
return fmt.Sprintf("unsupported type: %T", v)
}
}
Use a behavior-focused interface when possible
If your code only needs to write text, do not accept any; ask for the behavior you require:
func save(w io.Writer, text string) error {
_, err := fmt.Fprintln(w, text)
return err
}
io.Writer is better than any here because it documents the requirement and lets the compiler verify it.
Keep type assertions near the boundary
For example, after parsing loosely typed input, convert it to a well-defined struct early. This prevents any from spreading through the rest of the application.
type Config struct {
Port int
}
func readPort(raw any) (int, error) {
port, ok := raw.(int)
if !ok {
return 0, fmt.Errorf("port must be an int, got %T", raw)
}
return port, nil
}
Common Mistakes
Assuming interface{} has the methods of its stored value
This does not compile:
var value any = "hello"
fmt.Println(value.ToUpper()) // invalid: value has type any
The declared type is any, which has no methods. Assert the concrete type first, then use its operations:
text, ok := value.(string)
if ok {
fmt.Println(strings.ToUpper(text))
}
Using an unchecked type assertion
This can panic:
var value any = 10
text := value.(string)
Prefer the comma-ok form:
text, ok := value.(string)
if !ok {
fmt.Println("value is not a string")
return
}
fmt.Println(text)
Using any when a concrete type is known
Avoid vague fields when the payload always has one shape:
type Body struct {
Msg any
}
If it is always a LoginRequest, make that explicit:
type Body struct {
Msg LoginRequest
}
This gives compile-time checking and clearer code.
Forgetting that typed nil is different from a nil interface
var pointer *int = nil
var value any = pointer
fmt.Println(value == nil) // false
value is not a nil interface: it contains a value whose concrete type is *int, even though that pointer is nil. This matters in error handling and optional values.
Expecting XML or JSON libraries to infer every interface value automatically
An interface field may need extra type information, tags, or custom marshal/unmarshal code depending on the format and possible payload types. When the XML schema is known, concrete fields are usually simpler and safer.
Comparisons
| Feature | interface{} / any | Specific interface | Concrete type |
|---|---|---|---|
| Can store | Any Go value | Values with required methods | One specified type |
| Compile-time guarantees | Low | Verifies behavior | Strongest |
| Need type assertions to access concrete data | Often | Sometimes | No |
| Best use | Unknown or heterogeneous data | Depend on behavior | Known data shape |
| Example | fmt.Println(value) | io.Writer | string, LoginRequest |
any versus interface{}
They are identical:
var a any = 1
var b interface{} = 1
any was added as a readability alias. Older code and APIs commonly use interface{}.
Empty interface versus a non-empty interface
type Printer interface {
Print()
}
Printer accepts only types with a Print method. In contrast, any accepts every type, including int and string.
Choose a non-empty interface when your function needs behavior. Choose a concrete type when the data shape is known.
Cheat Sheet
// `any` and `interface{}` are the same type.
var value interface{} = 42
var other any = "hello"
// Assign any concrete Go value.
value = true
value = []int{1, 2, 3}
// Safe type assertion.
text, ok := value.(string)
// Type switch for multiple possible types.
switch v := value.(type) {
case string:
fmt.Println(v)
case int:
fmt.Println(v)
default:
fmt.Printf("unknown type: %T\n", v)
}
// Variadic function that accepts any number of values of any type.
func log(values ...any) {
fmt.Println(values...)
}
- Every Go type implements
interface{}because it requires zero methods. - Prefer
anyin new code when it improves readability; both forms are equivalent. - Prefer concrete types when the value's shape is known.
- Prefer a specific interface, such as
io.Reader, when you need behavior. - Use
value.(T)only when you are certain ofT; otherwise useresult, ok := value.(T). - A non-nil interface can contain a typed nil pointer.
FAQ
What does interface{} mean in Go?
It is the empty interface: a type that can hold a value of any Go type. In current Go, any is an exact alias for it.
Why can interface{} hold every type?
Interfaces require types to implement their listed methods. The empty interface lists no methods, so every type satisfies it.
Is any different from interface{}?
No. any is an alias added to make code easier to read. They compile and behave identically.
Why does fmt.Println accept strings, numbers, and structs?
Its parameters use ...any, meaning it accepts any number of arguments and every argument may be any concrete type.
How do I convert an interface{} to a string in Go?
Use a type assertion:
text, ok := value.(string)
Check ok before using text if the type may differ.
Can a type assertion panic?
Yes. value.(string) panics if value does not contain a string. The two-result form, text, ok := value.(string), avoids that panic.
Should struct fields use interface{}?
Only when they truly need to hold several unrelated types. If the field has a known shape, use a concrete type for safer and clearer code.
What does interface{}(nil) mean?
It is a nil interface with no concrete type and no value. It differs from an interface that contains a typed nil pointer.
Mini Project
Description
Build a small event formatter. It receives values stored as any, identifies their concrete types with a type switch, and returns readable messages. This resembles code at an application boundary, such as logging, event processing, or decoding flexible input.
Goal
Create a function that safely formats string, integer, and login-event values passed as any.
Requirements
Accept one value of type any.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.