Search code examples
go-gorm

Gorm - how to access nullable fields when using pointers?


I'm new to Go and still learning about optional struct fields.

I'm looking at the Gorm manual page where it gives an example of using pointers to indicate nullable fields (here)

If I strip down the example given so that it contains only a mandatory field and an optional field I'm left with something like this:

https://play.golang.com/p/lOLGWNVvq1l :

package main

import "fmt"

type User struct {
  Name         string
  Email        *string
}

func main() {
    user := User{Name: "Example"}
    
    // cannot use "example@example.com" (untyped string constant) as *string value in assignment
    user.Email = "example@example.com"
    
    // invalid operation: user.Email == "example@example.com" (mismatched types *string and untyped string)
    if user.Email == "example@example.com" {
       fmt.Println("foo")
    }
}

How would I perform operations on a record that I've just retrieved from the database?

I need to be able to check if a nullable field is set to some value. I also can't assign a value to it.

One approach that I've thought of is to use some sort of wrapping function to try and make things safer, like at https://play.golang.com/p/4YlpPwaXMkm where I have:

func UnwrapString(x *string) string {
    if x != nil {
        return *x
    }
    return ""
}

func WrapString(x string) *string {
    return &x
}

func main() {
    user := User{Name: "Example"}

    // can safely set an optional value that is currently null
    if UnwrapString(user.Email) == "example@example.com" {
        fmt.Println("hello world")
    }
    
    // can safely set a value if the existing Email is null
    user.Email = WrapString("example@example.com")
    
    // only safe because the value is set
    if *user.Email == "example@example.com" {
        fmt.Println("hello world")
    }
}

Working with nullable fields in Gorm seems like such a basic and common thing that I don't expect to have to DIY. What's the idiomatic Gorm way to do this?


Solution

  • An idiomatic way to check if a field is non-nil, and if so, compare the value:

    if user.Email != nil && *user.Email == "example@example.com" {
      fmt.Println("foo")
    }
    

    The reason this works, even if user.Email is nil (and you get no nil-pointer dereference panic), is because Go has short circuit evaluation, meaning if the first comparison falls through in this AND statement, the second won't be evaluated, because there is no way this AND statement will ever be true if the first value is already false.

    To do inline pointer assignments, the function you wrote is what I would do as well:

    func StrPtr(s string) *string {
      return &s
    }
    

    And you could then do:

    user.Email = StrPtr("example@example.com")