Reader Question: Understanding (*T)(nil) syntax

February 13, 2023

I got a great email from a reader (I didn’t ask their permission to share, so keeping it anonymous) in response to last week’s message Interface variables, pointing out the somewhat essoteric syntax mentioned at the end of the example section:

x = v              // x has value (*T)(nil) and dynamic type *T

The (*T)(nil) syntax is easily confusing. It has been for me, too. So let’s dig into that, and hopefully quell that confusion, once and for all!

Now, the full details of what’s going on here are still to come when we get to the section of the spec on conversions, so this will serve as a sneak peak.

If you’ve been reading for a while, you may recall a couple of weeks ago when I talked about typed and untyped constants. In that post, I demonstrated an explicitly typed constant:

const isValid = bool(true)

This example shows a case of a type conversion. bool(true) serves as a type conversion, converting the contents of the parenthesis (i.e. true) to a bool type (or if conversion is impossible, it will cause a compile-time error).

I believe this is fairly intuitive to most people. We see this sort of type conversion all over the place in Go. Here are a few random examples:

var x = int(123)      // Convert from untyped literal `123` to int
var y = int64(x)      // Convert from int to int64
var z = []byte("foo") // Convert an untyped string literal to a byte slice

Now, as we’ll actually see tomorrow, the name of a type can also be expressed in parenthesis. This means this is equivalent to the above code:

var x = (int)(123)      // Convert from untyped literal `123` to int
var y = (int64)(x)      // Convert from int to int64
var z = ([]byte)("foo") // Convert an untyped string literal to a byte slice

I hope the pattern is beginning to emerge now…

Most of the time these parenthesis are completely superfluous. But occasionally, they’re actually meaningful. As when dealing with pointer types! Here’s a contrived example:

var x = int(123)
var y = &x
var z = (*int)(y)

Without the parenthesis around *int, the * is interpreted as the multiplication operator.

So what does (*T)(nil) mean?

It’s a type conversion expression to convert nil to the type of *T, or a pointer to T. (T is commonly used as a stand-in for any type in documentation).

This means that that these two variable declarations are equivalent. They create a variable of type *int, with a value of nil.

var x *int
var y = (*int)(nil)

So why would you ever want the latter syntax? Well, because it doesn’t require assignment to a variable. This is commonly seen in conjunction with the blank identifier (which we’ll get to in time), to perform a sort of compile-time assertion that a type implements a desired interface (if this doesn’t make sense to you yet, don’t worry, we’re really jumping ahead here).

// Ensures that *MyCustomType satisfies the json.Marshaler interface, without
// allocating any memory.
var _ json.Marshaler = (*MyCustomType)(nil)

Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Type Conversion & struct tgs

We’ve already mentioned that struct tags are ignored when doing conversion between struct types. Now we see where that’s defined, along with an example: Conversions … Struct tags are ignored when comparing struct types for identity for the purpose of conversion: type Person struct { Name string Address *struct { Street string City string } } var data *struct { Name string `json:"name"` Address *struct { Street string `json:"street"` City string `json:"city"` } `json:"address"` } var person = (*Person)(data) // ignoring tags, the underlying types are identical I don’t think there’s much else to say about that, so let’s move on, and finish up this section.


Type casting in Go

Yesterday I answered a reader question that included some often confused terminology. It’s nothing to be ashamed of, and it’s quite a common mistake. But I thought this would be a good opportunity to clear it up. The question was: Is it possible for an end user to type cast a string into an error directly? Go doesn’t support type casting. Actually, that’s not entirely true. There’s no single definition of type casting.


Pointer Arithmetic

I mentioned a few days ago that pointer arithmetic is possible through the unsafe package. Here’s how to do it. Package unsafe … The function Add adds len to ptr and returns the updated pointer unsafe.Pointer(uintptr(ptr) + uintptr(len)) [Go 1.17]. The len argument must be of integer type or an untyped constant. A constant len argument must be representable by a value of type int; if it is an untyped constant it is given type int.

Get daily content like this in your inbox!

Subscribe