Support validation markers for slice/array elements
Overview
Currently, validation markers like gt, lt, required, email, maxlength, etc. can only be applied to single fields. This proposal suggests extending these markers to also validate individual elements within slices and arrays.
Current Limitations
Currently supported:
type User struct {
// +govalid:gt=18
Age int `json:"age"` // ✅ Single numeric field
// +govalid:email
Email string `json:"email"` // ✅ Single string field
}
Currently NOT supported:
type User struct {
// +govalid:gt=0
Scores []int `json:"scores"` // ❌ Each element in numeric slice
// +govalid:email
Emails []string `json:"emails"` // ❌ Each element in string slice
// +govalid:maxlength=50
Names []string `json:"names"` // ❌ Length constraint for each string
}
Proposed Feature
Numeric Validation
type GameData struct {
// +govalid:gt=0,lt=100
Scores []int `json:"scores"` // Each score in range 0-100
// +govalid:gte=0.0
Rates []float64 `json:"rates"` // Each rate >= 0
}
String Validation
type ContactInfo struct {
// +govalid:email
Emails []string `json:"emails"` // Each element is valid email
// +govalid:maxlength=100
Names []string `json:"names"` // Each name <= 100 characters
// +govalid:minlength=3,maxlength=20
Tags []string `json:"tags"` // Each tag 3-20 characters
}
Required Validation
type StringList struct {
// +govalid:required
Items []string `json:"items"` // Each element is non-zero value
}
Combined with Existing Features
type UserProfile struct {
// Validate both slice length + each element format
// +govalid:minitems=1,maxitems=3,email
Emails []string `json:"emails"`
// +govalid:minitems=1,required,minlength=2
Tags []string `json:"tags"`
}
Expected Generated Code
Numeric Validation
Input:
type User struct {
// +govalid:gt=0
Scores []int `json:"scores"`
}
Generated validation code:
func ValidateUser(t *User) error {
for _, score := range t.Scores {
if \!(score > 0) {
return ErrUserScoresGTValidation
}
}
return nil
}
String Validation
Input:
type Contact struct {
// +govalid:email
Emails []string `json:"emails"`
}
Generated validation code:
func ValidateContact(t *Contact) error {
for _, email := range t.Emails {
if \!validationhelper.IsValidEmail(email) {
return ErrContactEmailsEmailValidation
}
}
return nil
}
Target Markers and Types
Numeric Validation Markers
- Markers:
gt, gte, lt, lte
- Target types:
[]int, []float64, [N]int32, etc. (all numeric slice/array types)
String Validation Markers
- Markers:
email, maxlength, minlength
- Target types:
[]string, [N]string
Generic Validation Markers
- Markers:
required
- Target types: All slice/array types (zero value check for elements)
Error Messages
// Numeric validation
ErrUserScoresGTValidation = errors.New("all elements in field Scores must be greater than 0")
// String validation
ErrContactEmailsEmailValidation = errors.New("all elements in field Emails must be valid email addresses")
ErrUserNamesMaxLengthValidation = errors.New("all elements in field Names must be at most 100 characters")
// Required validation
ErrUserTagsRequiredValidation = errors.New("all elements in field Tags are required (cannot be zero value)")
Implementation Considerations
Type Detection Extension
// Current: Only direct types supported
func ValidateGT(pass *codegen.Pass, field *ast.Field, expressions map[string]string, structName string) validator.Validator {
typ := pass.TypesInfo.TypeOf(field.Type)
// Proposed: Support slice/array element types too
switch underlyingType := typ.Underlying().(type) {
case *types.Basic:
// Existing single field validation
case *types.Slice:
elementType := underlyingType.Elem()
// Validation for element type
case *types.Array:
elementType := underlyingType.Elem()
// Validation for element type
}
}
Performance
- Processing time proportional to number of elements
- Zero allocations maintained
- Empty slices/arrays skip validation
Special Cases
// Empty slice handling
var emptySlice []string // Skip validation
// Nil slice handling
var nilSlice []string = nil // Skip validation (controlled separately by required)
Usage Examples
type APIRequest struct {
// +govalid:required
// +govalid:minitems=1,maxitems=10
// +govalid:email
Recipients []string `json:"recipients"` // Required, 1-10 items, each must be valid email
// +govalid:minitems=1
// +govalid:gt=0,lte=100
Scores []int `json:"scores"` // At least 1 item, each element 0-100
// +govalid:maxlength=50,required
Tags []string `json:"tags"` // Each element required and <= 50 characters
}
Benefits
- Consistency: Extends existing marker syntax naturally
- Performance: Compile-time generation maintains zero allocations
- Type Safety: Full compile-time validation
- Flexibility: Combines with existing collection markers (
maxitems/minitems)
- Backward Compatibility: Existing single-field usage unchanged
Related Issues
This feature can be combined with existing markers:
Support validation markers for slice/array elements
Overview
Currently, validation markers like
gt,lt,required,email,maxlength, etc. can only be applied to single fields. This proposal suggests extending these markers to also validate individual elements within slices and arrays.Current Limitations
Currently supported:
Currently NOT supported:
Proposed Feature
Numeric Validation
String Validation
Required Validation
Combined with Existing Features
Expected Generated Code
Numeric Validation
Input:
Generated validation code:
String Validation
Input:
Generated validation code:
Target Markers and Types
Numeric Validation Markers
gt,gte,lt,lte[]int,[]float64,[N]int32, etc. (all numeric slice/array types)String Validation Markers
email,maxlength,minlength[]string,[N]stringGeneric Validation Markers
requiredError Messages
Implementation Considerations
Type Detection Extension
Performance
Special Cases
Usage Examples
Benefits
maxitems/minitems)Related Issues
This feature can be combined with existing markers:
maxitems/minitems(Issue MaxItems #7, MinItems #10)required