How to Create Values Dynamically with reflect.New
You are building a plugin loader. The configuration file says "driver": "postgres". Your code needs to create a PostgresDriver struct, but the compiler only sees the string "postgres". You cannot write new(PostgresDriver) because the type is hidden behind a string value. You need the program to allocate memory for a type it discovers while running. That is where reflect.New comes in.
reflect.New allocates a new zero-valued instance of a type described by a reflect.Type. It returns a reflect.Value holding a pointer to the fresh allocation. Use it when the type is unknown at compile time and must be determined from runtime data.
Concept and analogy
Normal Go code uses new(T) or &T{} to allocate memory. Those expressions require the type T to be present in the source code. The compiler resolves the size, layout, and zero value at build time.
reflect.New works with type metadata instead of type names. Think of reflect.TypeOf as scanning a blueprint and producing a description card. reflect.New takes that description card, builds the object on the heap, zeros out the memory, and hands you a key to the object wrapped in a reflect.Value box. You must call Interface() to open the box and get the actual Go pointer.
The result is always a pointer. If you pass the type of int, reflect.New returns a pointer to a fresh int set to zero. If you pass the type of a struct, you get a pointer to a struct where every field is zero.
Minimal example
Here is the simplest pattern: get the type description, allocate, and extract the pointer.
package main
import (
"fmt"
"reflect"
)
type Config struct {
Name string
Port int
}
func main() {
// TypeOf returns a reflect.Type describing the Config struct
t := reflect.TypeOf(Config{})
// New allocates a zero-valued Config on the heap and returns a reflect.Value holding a *Config
ptrVal := reflect.New(t)
// Interface() extracts the underlying Go value; type assertion converts reflect.Value to *Config
cfg := ptrVal.Interface().(*Config)
// Prints the zero-valued struct pointer
fmt.Printf("%+v\n", cfg)
}
reflect.New returns a reflect.Value, not the pointer directly. The Interface() method unwraps the value into an interface{}. The type assertion .(*Config) converts that interface to the concrete pointer type. This assertion is safe here because New always returns a pointer to the input type.
Walkthrough
The call chain has three distinct steps.
reflect.TypeOf(Config{}) inspects the zero value of Config and produces a reflect.Type. This object holds the name, kind, field list, and method set of the type. It does not hold an instance.
reflect.New(t) reads the size from the type description, allocates memory on the heap, fills it with zeros, and creates a reflect.Value that holds a pointer to that memory. The reflect.Value is addressable, which means you can mutate its contents using reflection methods.
ptrVal.Interface() returns the pointer as an interface{}. The runtime checks that the value inside the reflect.Value can be stored in an interface. The type assertion then checks that the interface holds a *Config. If the types match, you get the pointer. If not, the program panics.
Addressability and mutation
One reason to use reflect.New is that the result is addressable. Many reflection operations require an addressable value. If you call reflect.ValueOf(someVar) on a constant or a value retrieved from a map, the result is not addressable. You cannot set fields on it.
reflect.New guarantees addressability because it allocates fresh memory. You can dereference the pointer and modify fields dynamically.
// Elem() dereferences the pointer to access the struct fields
cfgVal := ptrVal.Elem()
// SetString mutates the Name field via reflection
cfgVal.FieldByName("Name").SetString("dynamic")
// SetInt mutates the Port field via reflection
cfgVal.FieldByName("Port").SetInt(8080)
This pattern is common in generic mappers that populate structs from JSON or database rows without knowing the struct type at compile time. The mapper calls reflect.New to get a fresh, addressable struct, then walks the fields and sets values.
Convention aside
Go developers treat reflection as a last resort. Reflection hides errors until runtime. The compiler cannot check field names, types, or method existence. If you find yourself using reflect.New in business logic, step back. Use a map[string]func() Interface or a factory pattern instead. Constructors fail at compile time or return clear error messages. Reflection forces you into interface{} land, which breaks the "accept interfaces, return structs" convention and makes code harder to test.
Realistic example
Real code often uses a registry to map strings to types. Here is a factory that creates driver instances by name. The registry stores reflect.Type values so the factory can allocate any registered driver.
type Driver interface {
Connect()
}
type Postgres struct{}
type MySQL struct{}
func (p *Postgres) Connect() { fmt.Println("Postgres") }
func (m *MySQL) Connect() { fmt.Println("MySQL") }
// Registry stores type descriptions, not instances
var drivers = map[string]reflect.Type{
"pg": reflect.TypeOf(Postgres{}),
"my": reflect.TypeOf(MySQL{}),
}
The factory looks up the type and allocates. It returns the result as the Driver interface.
func NewDriver(name string) Driver {
t, ok := drivers[name]
if !ok {
return nil
}
// Allocate a new instance and assert it implements Driver
return reflect.New(t).Interface().(Driver)
}
func main() {
d := NewDriver("pg")
if d != nil {
d.Connect()
}
}
The type assertion .(Driver) checks that the allocated type implements the interface. If you register a type that does not implement Driver, the assertion fails at runtime. The registry approach centralizes type knowledge and keeps allocation logic out of the caller.
Pitfalls and runtime errors
Reflection errors surface at runtime. A missing type or wrong assertion crashes the program.
Pass a nil type to reflect.New and the program crashes with panic: reflect: New(nil). Always verify the type exists before calling New. The registry lookup in the example handles this by checking the map result.
Forget that New returns a pointer. If you write reflect.New(t).Interface().(Config), the runtime panics with interface conversion: interface {} is *Config, not Config. The result is always a pointer. Assert to *Config, not Config.
Setting fields on a struct requires the field to be exported. If you try to set a lowercase field via reflection, the program panics with panic: reflect: Set of unexported field. Reflection cannot bypass Go's visibility rules.
Zero values can cause subtle bugs. reflect.New creates a struct where every field is zero. Maps and slices are nil. If code assumes a map is initialized, you get panic: assignment to entry in nil map. Initialize slices and maps manually after allocation if the logic requires them.
Decision matrix
Use reflect.New when you must allocate a type discovered at runtime, such as in a plugin system or a generic factory. Use new(T) or &T{} when the type is known at compile time; static allocation is faster and safer. Use a map of constructor functions when you need custom initialization logic for each type. Use an interface with a factory method when you want to avoid reflection entirely and keep the code testable.
Static types first. Reflection only when the type is truly unknown.