Go Anonymous Structs — Tasks¶
Instructions¶
Each task includes a description, starter code, expected output, and an evaluation checklist. Use anonymous structs idiomatically. Promote to a named type when the task hints toward reuse, methods, or a public API.
Task 1 — Inline Point¶
Difficulty: Beginner Topic: Basic anonymous struct value
Description: Build an anonymous struct value p with two integer fields X and Y, initialize them to 3 and 4, and print the sum.
Starter Code:
package main
import "fmt"
func main() {
// TODO: declare anonymous struct p with X=3, Y=4
var p struct{ X, Y int }
fmt.Println(p.X + p.Y)
}
Expected Output:
Evaluation Checklist: - [ ] Uses an anonymous struct literal. - [ ] Initializes both fields. - [ ] Prints the correct sum.
Task 2 — Test Table¶
Difficulty: Beginner Topic: Table-driven tests
Description: Write a test for a square(int) int function using a table-driven test with an anonymous struct row type. Cover at least three cases.
Starter Code:
package main
import "testing"
func square(x int) int { return x * x }
func TestSquare(t *testing.T) {
// TODO: cases as []struct{ in, want int }{...}
}
Expected Output: All cases pass.
Evaluation Checklist: - [ ] Anonymous struct with in and want fields. - [ ] At least three cases. - [ ] Loop iterates each case. - [ ] Failure message includes input and expected.
Task 3 — Inline JSON Body¶
Difficulty: Beginner Topic: One-off JSON shape
Description: Build a JSON request body containing name and count and print it as a string.
Starter Code:
package main
import (
"encoding/json"
"fmt"
)
func main() {
// TODO: anonymous struct with json tags, marshal, print
}
Expected Output:
Evaluation Checklist: - [ ] Anonymous struct with two fields. - [ ] Field tags match expected JSON keys. - [ ] Uses json.Marshal.
Task 4 — Inline JSON Decode (Pluck One Field)¶
Difficulty: Beginner Topic: Decoding into a one-field anonymous struct
Description: Given the JSON string {"name":"ada","age":36,"email":"a@x"}, decode only the name field into a one-field anonymous struct and print it.
Starter Code:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
body := `{"name":"ada","age":36,"email":"a@x"}`
// TODO: decode only the "name" field
_ = json.NewDecoder(strings.NewReader(body))
}
Expected Output:
Evaluation Checklist: - [ ] Anonymous struct with a single tagged field. - [ ] Uses json.NewDecoder. - [ ] Prints the decoded name.
Task 5 — Slice of Anonymous Records¶
Difficulty: Beginner Topic: Slice with elided element type
Description: Build a slice of anonymous-struct records (Path string, Size int64). Compute the total size and print it.
Starter Code:
package main
import "fmt"
func main() {
rows := []struct {
Path string
Size int64
}{
// TODO
}
var total int64
for _, r := range rows {
total += r.Size
}
fmt.Println(total)
}
Expected Output: any non-zero number depending on rows.
Evaluation Checklist: - [ ] At least three rows. - [ ] Element type elided in inner literals. - [ ] Total computed correctly.
Task 6 — Embedded Sub-Struct¶
Difficulty: Beginner-Intermediate Topic: Anonymous struct as a field type
Description: Define a named type Job with two fields: ID int and Meta (anonymous struct with StartedAt time.Time and Owner string). Initialize a value and print the owner.
Starter Code:
package main
import (
"fmt"
"time"
)
type Job struct {
// TODO
}
func main() {
j := Job{ID: 1}
j.Meta.StartedAt = time.Now()
j.Meta.Owner = "ada"
fmt.Println(j.Meta.Owner)
}
Expected Output:
Evaluation Checklist: - [ ] Named outer type. - [ ] Anonymous-struct field Meta. - [ ] Field access via j.Meta.Owner.
Task 7 — Map With Anonymous Struct Key¶
Difficulty: Intermediate Topic: Anonymous struct as map key
Description: Build a map keyed by an anonymous struct (X, Y int) mapping to a string description. Insert and look up two entries. Print the look-ups.
Starter Code:
package main
import "fmt"
func main() {
// TODO
var m map[struct{ X, Y int }]string
_ = m
fmt.Println()
}
Expected Output: depends on chosen keys.
Evaluation Checklist: - [ ] Map key is anonymous struct. - [ ] Uses literal {X: ..., Y: ...} for inserts. - [ ] Looks up via the same shape.
Task 8 — Anonymous Struct in a Channel¶
Difficulty: Intermediate Topic: Channel of anonymous structs
Description: Build a buffered channel whose element type is struct { Tick int; At time.Time }. Send three messages from a goroutine and receive them in main. Print each.
Starter Code:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan struct {
Tick int
At time.Time
}, 4)
// TODO: goroutine sends, main receives
_ = ch
fmt.Println()
}
Expected Output: three lines of <tick> <time>.
Evaluation Checklist: - [ ] Channel of anonymous struct. - [ ] Goroutine sends. - [ ] Main receives all messages.
Task 9 — Refactor Decision¶
Difficulty: Intermediate Topic: Promote anonymous to named
Description: Take the following code and decide whether to promote the anonymous struct to a named type. Justify in a comment, then apply your decision.
Starter Code:
package main
import "fmt"
func newUserA() (struct {
ID int
Email string
}, error) {
return struct {
ID int
Email string
}{ID: 1, Email: "a@x"}, nil
}
func newUserB() (struct {
ID int
Email string
}, error) {
return struct {
ID int
Email string
}{ID: 2, Email: "b@x"}, nil
}
func main() {
a, _ := newUserA()
b, _ := newUserB()
fmt.Println(a, b)
}
Expected Output (after refactor): {1 a@x} {2 b@x}.
Evaluation Checklist: - [ ] Promote to a named User type because the shape is reused. - [ ] Both functions return the named type. - [ ] Printed output matches. - [ ] Brief comment explaining the choice.
Task 10 — Inline Audit Log¶
Difficulty: Intermediate Topic: One-off logging payload
Description: Inside a function audit(action string, userID int), build an anonymous-struct payload with at (current Unix time), action, and user_id fields, marshal as JSON, and print to stdout.
Starter Code:
package main
import (
"encoding/json"
"fmt"
"time"
)
func audit(action string, userID int) {
// TODO
_ = json.Marshal
_ = time.Now
_ = fmt.Println
}
func main() {
audit("login", 7)
}
Expected Output example:
Evaluation Checklist: - [ ] Anonymous struct with three tagged fields. - [ ] Marshals as JSON. - [ ] Prints the JSON.
Task 11 — Detect Anonymity via Reflection¶
Difficulty: Intermediate-Advanced Topic: Reflection on anonymous types
Description: Write a function isAnonymous(v any) bool that returns true if the value's static type is an anonymous struct, false otherwise (named struct or non-struct).
Starter Code:
package main
import (
"fmt"
"reflect"
)
func isAnonymous(v any) bool {
// TODO
_ = reflect.TypeOf
return false
}
type Point struct{ X int }
func main() {
fmt.Println(isAnonymous(struct{ A int }{1})) // true
fmt.Println(isAnonymous(Point{1})) // false
fmt.Println(isAnonymous(42)) // false
}
Expected Output:
Evaluation Checklist: - [ ] Uses reflect.TypeOf. - [ ] Checks Kind() == reflect.Struct. - [ ] Checks Name() == "".
Task 12 — Tag-Driven Difference¶
Difficulty: Advanced Topic: Type identity with tags
Description: Write a program that demonstrates two anonymous structs with the same fields but different tags being non-assignable. Show that a conversion via a named alias does not bypass the rule.
Starter Code:
package main
import "fmt"
func main() {
// TODO: declare a, b with same fields, different tags
// Try a = b — comment which line fails.
fmt.Println()
}
Expected Outcome: a compile error (or commented-out failing line) demonstrating tag-driven type difference.
Evaluation Checklist: - [ ] Two anonymous structs with identical fields but different tags. - [ ] Attempted assignment is a compile error. - [ ] Brief comment explaining structural identity with tags.
Task 13 — Promote a Wide Test Table¶
Difficulty: Advanced Topic: Refactor
Description: Take the following 11-field test table and refactor into a named userCase type. Use named-field initialization to highlight the relevant fields per row.
Starter Code:
package main
import "testing"
func TestPipeline(t *testing.T) {
cases := []struct {
name string
userID int
orgID int
roleID int
email string
active bool
banned bool
timeout int
expectErr string
setup func()
teardown func()
}{
{"happy", 1, 2, 3, "a@x", true, false, 5, "", nil, nil},
{"banned", 1, 2, 3, "a@x", true, true, 5, "user banned", nil, nil},
{"timeout", 1, 2, 3, "a@x", true, false, 0, "deadline", nil, nil},
}
_ = cases
_ = t
}
Expected Outcome: Same logic, named type, named-field initialization.
Evaluation Checklist: - [ ] Defines named userCase type. - [ ] Cases use named-field initialization. - [ ] Default-zero fields are not spelled. - [ ] Comment explaining why the refactor improves readability.
Task 14 — Anonymous-Struct-Backed sync.Pool¶
Difficulty: Advanced Topic: Pool of one-off shapes
Description: Build a sync.Pool whose New returns an anonymous struct with a 4 KB byte buffer. Use it from two goroutines and print each goroutine's checked-out buffer length. Then refactor to use a named type and explain why naming is preferable.
Starter Code:
package main
import (
"fmt"
"sync"
)
var pool = sync.Pool{
New: func() any {
return &struct {
Buf [4096]byte
}{}
},
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// TODO: get from pool, print len, put back
}()
}
wg.Wait()
_ = fmt.Println
}
Expected Output:
Evaluation Checklist: - [ ] Both goroutines get and put back to pool. - [ ] Refactored to a named type. - [ ] Brief explanation of why named is preferable for sync.Pool.
Task 15 — Build a Linter Sketch¶
Difficulty: Advanced Topic: AST analysis
Description: Sketch (not full implementation) a go/ast walker that flags anonymous struct types appearing in exported function signatures. Provide a function checkFile(path string) []string returning a slice of warning messages.
Starter Code:
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)
func checkFile(path string) []string {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, path, nil, 0)
if err != nil {
return []string{err.Error()}
}
var warnings []string
ast.Inspect(file, func(n ast.Node) bool {
// TODO: find FuncDecl, check Name is exported,
// walk Type.Params and Type.Results for *ast.StructType.
return true
})
return warnings
}
func main() {
_ = fmt.Println
}
Expected Outcome: When run on a file containing func Public() struct{ X int }, the linter emits a warning. When run on a file with only named types in exported signatures, no warnings.
Evaluation Checklist: - [ ] Walks AST. - [ ] Identifies exported function declarations. - [ ] Detects *ast.StructType in params or results. - [ ] Emits a clear warning message with file position.
Hints¶
- For tasks involving JSON, remember
json.Marshalworks on any struct; tag rules are the same as for named types. - For tasks involving reflection,
reflect.TypeOf(v).Name() == ""is the cleanest anonymity check. - For refactor tasks, a 4–5 field threshold is a reasonable rule.
- For the linter sketch,
*ast.FieldList.List[i].Typeis where to look for struct types.
Summary¶
These tasks cover the full lifecycle of anonymous structs: - Construction (Task 1, 5, 7, 8). - Tests (Task 2, 13). - Serialization (Task 3, 4, 10). - Reflection (Task 11). - Identity rules (Task 12). - Refactoring decisions (Task 9, 13, 14). - Tooling (Task 15).
Working through them gives you both the mechanics and the design judgment to use anonymous structs idiomatically.