Is it idiomatic to use new type wrappers in Go for IDs?

Viewed 550

In other statically-typed languages like Haskell, Scala, or Rust, sometimes for things like identifiers, one would wrap them in special types to help the compiler catch errors (e.g. preventing you from passing in another in-scope String) and to improve readability of the code by encoding responsibility at the type-level.

Some examples:

newtype UserId = UserId Integer

or

case class UserId(value: Long) extends AnyVal

or

struct UserId(u64);

All of these offer (usually) no runtime costs and little added clutter for improved peace of mind and dev-time documentation at the type level.

Go also has this ability:

type UserId uint64

My question is not whether or not this is possible, but whether this is something that is widespread and/or considered idiomatic or not in Go, and why.

1 Answers

Go supports type inference on compiler level. There is no runtime cost in the compiled binary.

With this type definition

type UserID uint64

Both of the following should result in the same binary:

uid := UserID(1)
var uid UserID = 1

And to my experience, development environment can understand both equally well. And they are both humanly-readable. I think I see more of the first one in production code than the second one.

As @AndySchweig mentioned in comment, this is usually used when the variable is a Enum like value.

type UserType uint

const (
  TypeVisitor UserType = 1
  TypeNormal = 2
  TypeAdmin = 3
)

or

type UserType uint

const (
  TypeVisitor UserType = iota
  TypeNormal
  TypeAdmin
)

The exported const would make code much more readable:


switch userType {
  case mylib.TypeVisitor:
    // do something
  case mylib.TypeNormal:
    // do something
  case mylib.TypeAdmin:
    // do something
}

Related