Alex

Reflection (Video 33)

interface{}

var w io.Writer = os.Stdout
f := w.(*os.File) // success
c := w.(*bytes.Buffer) // failure since the interface holds a *os.File not a *bytes.Buffer
c, ok := w.(*bytes.Buffer)
func Println(args ...interface{}) {
    // ...
    for _, arg := range args {
        switch a := arg.(type) {
            case string: // concrete type
                // do something with the string
            case Stringer: // interface
                // call the String() method on the interface to get the string representation
        }
    }
}

DeepEqual

want := struct{
    someSlice := []int{1,2,3}
}

got := someFunction()

if !reflect.DeepEqual(got, want) {
    fmt.Println("failed equality check")
}

Reflection in JSON Unmarshalling

{
    "item": "album",
    "album": {"title": "Quality Over Opinion"}
}
{
    "item": "song",
    "song": {"title": "Shallow Laughter", "artist": "Louis Cole"}
}
type Response struct {
    Item string
    Album string
    Title string
    Artist string
}

Custom Unmarshaller Caveat

type Person struct {
    Name string `json:"name"`
}

func (p *Person) UnmarshalJSON(data []byte) error {
    // uh oh stinky, this will recursively unmarshal
    return json.Unmarshal(data, p)
}

func (p *Person) UnmarshalJSON(data []byte) error {
    type wrapper Person
    // we make a new variable aux which is a wrapper type. The pointer points to the same underlying memory
    // of p, however in the type system the UnmarshalJSON method isn't defined for the wrapper type
    // and so we can call json.Unmarshal without issues. Then we can do any custom validation we want to
    aux := (*wrapper)(p)

    if err := json.Unmarshal(data, aux); err != nil {
        return err
    }

    // custom validation / logic
    if p.Name == "" {
        return errors.New("missing name")
    }

    return nil
}