Nested Structs
Working with embedded and nested struct types.
Embedded Structs
Embedded structs are stored in the Nested map:
type Address struct {
Street string
City string
ZipCode string
}
type User struct {
Name string
Email string
Address Address // Embedded struct
}
atomizer, _ := atom.Use[User]()
user := &User{
Name: "Alice",
Email: "alice@example.com",
Address: Address{
Street: "123 Main St",
City: "Springfield",
ZipCode: "12345",
},
}
atom := atomizer.Atomize(user)
// atom.Strings["Name"] = "Alice"
// atom.Strings["Email"] = "alice@example.com"
// atom.Nested["Address"] = Atom{
// Strings: {"Street": "123 Main St", "City": "Springfield", "ZipCode": "12345"}
// }
Accessing Nested Atoms
// Access the nested atom
addressAtom := atom.Nested["Address"]
// Read nested fields
street := addressAtom.Strings["Street"]
city := addressAtom.Strings["City"]
fmt.Println(street) // "123 Main St"
fmt.Println(city) // "Springfield"
Pointer to Struct
Pointer fields allow optional nested structs:
type User struct {
Name string
Address *Address // Optional
}
atomizer, _ := atom.Use[User]()
// With address
user := &User{
Name: "Alice",
Address: &Address{Street: "123 Main St", City: "Springfield"},
}
atom := atomizer.Atomize(user)
// atom.Nested["Address"] exists
// Without address
user = &User{Name: "Bob", Address: nil}
atom = atomizer.Atomize(user)
// atom.Nested["Address"] does not exist
Checking for Nil
if addressAtom, ok := atom.Nested["Address"]; ok {
fmt.Println("Has address:", addressAtom.Strings["City"])
} else {
fmt.Println("No address")
}
Slice of Structs
Slices of structs are stored in NestedSlices:
type Order struct {
ID int64
Items []OrderItem
}
type OrderItem struct {
ProductID int64
Quantity int64
Price float64
}
atomizer, _ := atom.Use[Order]()
order := &Order{
ID: 1001,
Items: []OrderItem{
{ProductID: 1, Quantity: 2, Price: 19.99},
{ProductID: 2, Quantity: 1, Price: 49.99},
},
}
atom := atomizer.Atomize(order)
// atom.Ints["ID"] = 1001
// atom.NestedSlices["Items"] = [
// Atom{Ints: {"ProductID": 1, "Quantity": 2}, Floats: {"Price": 19.99}},
// Atom{Ints: {"ProductID": 2, "Quantity": 1}, Floats: {"Price": 49.99}},
// ]
Iterating Nested Slices
for i, itemAtom := range atom.NestedSlices["Items"] {
productID := itemAtom.Ints["ProductID"]
quantity := itemAtom.Ints["Quantity"]
price := itemAtom.Floats["Price"]
fmt.Printf("Item %d: product=%d qty=%d price=%.2f\n",
i, productID, quantity, price)
}
Deeply Nested Structures
Atom handles arbitrary nesting depth:
type Company struct {
Name string
Departments []Department
}
type Department struct {
Name string
Manager Employee
Staff []Employee
}
type Employee struct {
Name string
Title string
Salary float64
}
atomizer, _ := atom.Use[Company]()
company := &Company{
Name: "Acme Corp",
Departments: []Department{
{
Name: "Engineering",
Manager: Employee{Name: "Alice", Title: "VP", Salary: 150000},
Staff: []Employee{
{Name: "Bob", Title: "Senior", Salary: 120000},
{Name: "Carol", Title: "Junior", Salary: 80000},
},
},
},
}
atom := atomizer.Atomize(company)
// atom.Strings["Name"] = "Acme Corp"
// atom.NestedSlices["Departments"][0].Strings["Name"] = "Engineering"
// atom.NestedSlices["Departments"][0].Nested["Manager"].Strings["Name"] = "Alice"
// atom.NestedSlices["Departments"][0].NestedSlices["Staff"][0].Strings["Name"] = "Bob"
Self-Referential Types
Atom handles recursive type definitions:
type Node struct {
Value int64
Children []Node
}
atomizer, _ := atom.Use[Node]()
tree := &Node{
Value: 1,
Children: []Node{
{Value: 2, Children: nil},
{Value: 3, Children: []Node{
{Value: 4, Children: nil},
}},
},
}
atom := atomizer.Atomize(tree)
// atom.Ints["Value"] = 1
// atom.NestedSlices["Children"][0].Ints["Value"] = 2
// atom.NestedSlices["Children"][1].Ints["Value"] = 3
// atom.NestedSlices["Children"][1].NestedSlices["Children"][0].Ints["Value"] = 4
Pointer Self-Reference
type LinkedNode struct {
Value int64
Next *LinkedNode
}
atomizer, _ := atom.Use[LinkedNode]()
list := &LinkedNode{
Value: 1,
Next: &LinkedNode{
Value: 2,
Next: nil,
},
}
atom := atomizer.Atomize(list)
// atom.Ints["Value"] = 1
// atom.Nested["Next"].Ints["Value"] = 2
// atom.Nested["Next"].Nested["Next"] does not exist (nil)
Slice of Pointer to Struct
type Team struct {
Name string
Members []*Person
}
type Person struct {
Name string
Role string
}
atomizer, _ := atom.Use[Team]()
team := &Team{
Name: "Alpha",
Members: []*Person{
{Name: "Alice", Role: "Lead"},
{Name: "Bob", Role: "Developer"},
},
}
atom := atomizer.Atomize(team)
// Works the same as []Person
// atom.NestedSlices["Members"][0].Strings["Name"] = "Alice"
Building Nested Atoms Manually
// Create parent atomizer
orderAtomizer, _ := atom.Use[Order]()
itemAtomizer, _ := atom.Use[OrderItem]()
// Build nested atoms
item1 := itemAtomizer.NewAtom()
item1.Ints["ProductID"] = 1
item1.Ints["Quantity"] = 2
item1.Floats["Price"] = 19.99
item2 := itemAtomizer.NewAtom()
item2.Ints["ProductID"] = 2
item2.Ints["Quantity"] = 1
item2.Floats["Price"] = 49.99
// Assemble parent
orderAtom := orderAtomizer.NewAtom()
orderAtom.Ints["ID"] = 1001
orderAtom.NestedSlices["Items"] = []atom.Atom{*item1, *item2}
// Deatomize
order, _ := orderAtomizer.Deatomize(orderAtom)
Spec Propagation
Nested atoms carry their own type specs:
atom := atomizer.Atomize(user)
fmt.Println(atom.Spec.TypeName) // "User"
fmt.Println(atom.Nested["Address"].Spec.TypeName) // "Address"
Best Practices
Flatten When Possible
For simple cases, consider flattening:
// Deeply nested (harder to query)
type User struct {
Profile struct {
Contact struct {
Email string
}
}
}
// Flattened (easier to query)
type User struct {
ProfileContactEmail string
}
Use Pointers for Optional
// Good - explicit optionality
type User struct {
BillingAddress *Address // Optional
ShippingAddress Address // Required
}
// Ambiguous - is empty address intentional or missing?
type User struct {
BillingAddress Address
ShippingAddress Address
}
Limit Nesting Depth
Deep nesting increases complexity:
// Hard to work with
atom.NestedSlices["A"][0].Nested["B"].NestedSlices["C"][0].Strings["D"]
// Consider restructuring or using IDs/references
Next Steps
- Interfaces Guide - Custom serialization
- Testing Guide - Testing atom-based code