go
59 lines · 7 steps
Struct validation with reflection in Go
A tiny library that reads `validate` struct tags and checks each field against named rules using reflection.
Explained by
highlit
1package validate
2
3import (
4 "fmt"
5 "reflect"
6 "strconv"
7 "strings"
8)
9
10type Error struct {
11 Field string
12 Rule string
13}
14
15func (e Error) Error() string {
16 return fmt.Sprintf("field %q failed rule %q", e.Field, e.Rule)
17}
18
19func Struct(v interface{}) []error {
20 val := reflect.ValueOf(v)
21 if val.Kind() == reflect.Ptr {
22 val = val.Elem()
23 }
24 typ := val.Type()
25
26 var errs []error
27 for i := 0; i < typ.NumField(); i++ {
28 field := typ.Field(i)
29 tag := field.Tag.Get("validate")
30 if tag == "" || tag == "-" {
31 continue
32 }
33 fv := val.Field(i)
34 for _, rule := range strings.Split(tag, ",") {
35 name, arg, _ := strings.Cut(rule, "=")
36 if !check(fv, name, arg) {
37 errs = append(errs, Error{Field: field.Name, Rule: rule})
38 }
39 }
40 }
41 return errs
42}
43
44func check(fv reflect.Value, name, arg string) bool {
45 switch name {
46 case "required":
47 return !fv.IsZero()
48 case "min":
49 n, _ := strconv.Atoi(arg)
50 return fv.Kind() == reflect.String && len(fv.String()) >= n
51 case "max":
52 n, _ := strconv.Atoi(arg)
53 return fv.Kind() == reflect.String && len(fv.String()) <= n
54 case "email":
55 return strings.Contains(fv.String(), "@")
56 default:
57 return true
58 }
59}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Struct tags let you attach declarative metadata that code reads at runtime via reflection.
- 2Collecting errors into a slice reports every failure at once instead of stopping at the first.
- 3A rule dispatcher keyed on a name string makes the validator easy to extend with new checks.
Related explainers
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
go
package streaming import ( "bufio"
Streaming NDJSON logs over HTTP in Go
http-streaming
channels
select
Advanced
10 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
go
package api import ( "crypto/sha256"
ETag conditional requests in Gin
http-caching
etag
conditional-requests
Intermediate
6 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/struct-validation-with-reflection-in-go-explained-go-1973/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.