# goref: express the non-nil pointer annotation in go with a generic alias

i don't write a lot of typescript but i occasionally dabble in it. i wrote this typescript code recently:

  // maybeVariableX and maybeVariableY type is string | null.
  // variableZ type is string.
  // then i had this code:
  if (maybeVariableX != null) {
    variableZ = maybeVariableY
  }

i got this error:

  Type 'string | null' is not assignable to type 'string'.

i was... pleasantly surprised that this was caught. amazing! i wanted to have maybeVariableY in the condition, i just had a typo.

this thing is called "union types" in typescript. i don't really want that in go. but is it possible to have similar nil-check in go?

i found a nice suggestion to use & for non-nil pointers here: https://getstream.io/blog/fixing-the-billion-dollar-mistake-in-go-by-borrowing-from-rust/. but that requires a language change, that's too big of a change.

based on https://go.dev/blog/alias-names now i could have a simple package like this to represent pointers that should not be nil:

  package ref

  type Ref[T any] = *T

it doesn't do anything in the go compiler, it doesn't create a new type. i can assign Ref[T] to *T and vice versa just fine. now i could write a code like this:

  func f(s ref.Ref[string]) {
    fmt.Println(*s)
  }

  func g(s *string) {
    f(s)
  }

this compiles just fine. it has a semantic problem though: g takes a potentially nil pointer and calls f which wants a non-nil pointer. but a sufficiently smart linter could give a warning here similarly to typescript above! and it wouldn't give the same warning for this code:

  func g(s *string) {
    if s != nil {
      f(s)
    }
  }

is this useful? would i use it? i don't know. but i wouldn't mind playing with it.

i'm not up for writing such a linter though. just wanted to express the desire that i'd like to try such a linter.

note 1: technically you could have achieve this previously without generic aliases too by writing `alias RefT = *T`. the downside of that is that you need to do that explicitly for each type. or you could use some special `/*LINT:nonil*/` comment next to the var where you want non-nils. the downside of that is that it doesn't get included in the generated godoc so users might miss it. both of these lack the right ergonomics. i think the `type Ref[T any] = *T` might be just simple enough that it can catch on.

note 2: i can imagine using such aliases for other linter-only annotations too such as const, e.g. `type Const[T] = T`. not that i want const annotations. i fear go is getting too complex.

published on 2024-09-30


posting a comment requires javascript.

to the frontpage