go-ozzo / ozzo-validation

An idiomatic Go (golang) validation package. Supports configurable and extensible validation rules (validators) using normal language constructs instead of error-prone struct tags.
MIT License
3.73k stars 224 forks source link

Validate() method of embedded struct is not getting called when using pointer receiver #75

Closed felix-h closed 4 years ago

felix-h commented 5 years ago

When using embedded structs, the Validate() method of an embedded struct is not getting called when using a pointer receiver for the Validate() method.

https://play.golang.org/p/cLWuqkP8-mY

package main

import (
  "fmt"

  validation "github.com/go-ozzo/ozzo-validation"
)

type EmbeddedStruct struct {
  EmbeddedProperty string
}

func (embeddedStruct *EmbeddedStruct) Validate() error {
  fmt.Println("I am not getting printed :(") // <--- Bug: Validate method not getting called
  return nil
}

type Outer struct {
  EmbeddedStruct
  OuterProperty string
}

func (outer *Outer) Validate() error {
  fmt.Println("Validating outer struct")
  return validation.ValidateStruct(outer,
    validation.Field(&(outer.EmbeddedStruct)))
}

func main() {
  outer := Outer{}
  validation.Validate(&outer)
}

This prints: "Validating outer struct Program exited."

Expected output: "Validating outer struct I am not getting printed :( Program exited."

FWIW, the problem might be the type assertion in validation.go which fails for embedded structs:

if v, ok := value.(Validatable); ok { return v.Validate() }

felix-h commented 5 years ago

Changing "func (embeddedStruct *EmbeddedStruct) Validate() error" for "func (embeddedStruct EmbeddedStruct) Validate() error" fixes the problem.

qiangxue commented 4 years ago

This is because EmbeddedStruct within Outer is not a pointer. As a result, it's not implementing the Validatable interface because you associate Validate() with a pointer of EmbeddedStruct. This is golang rule. You can use the approach you described to fix the problem. You may also use *EmbeddedStruct within Outer.