0

I'm curious on what kind of patterns should used in Go for such cases. So let's suppose, you have a Request struct that holds some information for making an HTTP request, along with the parameters that can be sent in the payload and their respective validations. Now different type of validations would be applicable on the basis of the data type of the parameter, so I decided to use a map[string]Validations, where I could store the type of Validations mentioned in the Validations union (basically, being able to accomodate both IntValidations & StrValidations in the same map).

But the below code would result in an error because Validations can only be used as a type parameter, so how can we achieve this in Go? In Java this could be easily achieved with polymorphism, where Validation would be the parent class and int and float validations would be the child classes, so later on we could easily instantiate a HashMap of Validations.

Note: I would want to avoid the usage of map[string]interface{}.

type Validations interface {
IntValidations | StrValidations
}

type IntValidations struct {
  MaxVal int
  MinVal int
}

type StrValidations struct {
  MinLength int
  MaxLength int
  Regex string 
}

type Request struct {
 Path string 
 Payload map[string]Validations // Error: Validations cannot be used outside of a type constraint.
}
3
  • 3
    Usemap[string]Validation where Validation is an interface with methods appropriate for a validation. Example type Validation interface { Do(<insert args here>) error } Commented Jun 23 at 19:36
  • I see, but how will I be able to utilise this and populate IntValidations and StrValidations structs with this.. Could you please elaborate a bit more on this? Are you suggesting to get rid of the two structs and instead perform validations in the Do method?
    – ansme
    Commented Jun 23 at 20:12
  • 2
    Implement Do methods on the IntValidation and StrValidations types. See the Tour of Go for information on how to implement a method and how methods relate to interfaces. Commented Jun 23 at 20:55

1 Answer 1

3

As pointed out by @Cerise Limón in the comments, you can do it like so

package main

type Validators interface {
    Validate(value any) error // Can return bool also
}

type IntValidator struct {
    MaxVal int
    MinVal int
}

func (intValidator *IntValidator) Validate(value any) error {
    // run validations
    return nil
}

type StrValidator struct {
    MinLength int
    MaxLength int
    Regex     string
}

func (strValidator *StrValidator) Validate(value any) error {
    // run validations
    return nil
}

type Request struct {
    Path    string
    Payload map[string]Validations
}

Now you can add validators with

func main() {
    request := Request{Payload: make(map[string]Validations)}
    request.Payload["myIntVar"] = &IntValidations{MinVal: 10, MaxVal: 20}
    request.Payload["myStrVar"] = &IntValidations{MinLen: 10, MaxLen: 15, Regex: "[a-z]+"}

    if err := request.Payload["myIntVar"].Validate(13); err != nil {
        panic("could not validate")
    }
}
7
  • Great, this is exactly what I was looking for. But just one more follow up question. What if I there was a case where one would want to create a similar map accomodating multiple similar types, but contrary to this specific use case, the use of a method such as Validate is not required at all. How would we achieve the same then?
    – ansme
    Commented Jun 24 at 1:57
  • @ansme Describe what makes the types "similar". Commented Jun 24 at 2:13
  • @CeriseLimón Let's just say, it's a map of map[string]Animal, where we could hold similar types as values such as type Cat struct, type Dog struct, where these structs just hold basic fields like name string, neutered bool etc.. but let us assume that they don't require any functionality... therefore, the underlying interface Animal does not have any methods defined to it. Then how will we be able to create a map[string]Animal?
    – ansme
    Commented Jun 24 at 2:28
  • 1
    @ansme Follow the pattern in this answer, but use an empty Validate method. Commented Jun 24 at 2:46
  • 1
    @asme, you can make a simple Validate method that has 0 args, returns nothing and implement it with empty body to specify that it implemeta the Validator interface but does nothing. Commented Jun 24 at 2:59

Not the answer you're looking for? Browse other questions tagged or ask your own question.