Blank field names

March 10, 2023

Yesterday we started talking about Go’s structs, and breezed over a phrase about non-blank field names. Wassat?

Struct types

… Within a struct, non-blank field names must be unique.

Go has a concept of a blank identifier. It looks like the underscore character (_), and is useful in many situations, and we’ll discuss more of them in due time. (It’s also decidedly not useful in some situations where it’s permitted, and I made a video about that a while ago)

For now, we’re only concerned with blank field names. Here’s what one might look like:

type Person struct {
	Name string
	Age  int
	_    int64

Okay. But why would you ever want that?

Honestly, you probably never do.

But before we dive into the few use cases I know for blank field names, let’s discuss what it actually does:

First off, it does actually affect the struct. The field does exist. And it does take up memory, and it does hold a value:

	var x struct {
		Name string
	var y struct {
		Name string
		_    int64
	fmt.Println(x, y)                               // Prints: {} { 0}
	fmt.Println(unsafe.Sizeof(x), unsafe.Sizeof(y)) // Prints: 16 24

However, that blank-named field, which definitely does exist, and does take up memory… can never be accessed. You can’t set it to anything other than the default, zero value. The only way to access it is via reflection (which is what fmt.Println does under the hood to display { 0} above.)

So when is it useful?

One case where it’s useful is when using CGO, and mapping C structures to Go, when not all of the data in the C structure is useable by Go. By using the blank identifier, one can effectivly “skip” some of the in-memory representation of a struct. This is discussed briefly in this old GitHub issue. Mere mortals need not bother with this frequently.

There are two other places where I’ve seen a blank field name used as well. Although arguably, any unexported field would work for either of these cases:

  • To prevent the use of implicit field names in struct declaration. This requires a short explanation. Normally there are two, equivalent ways of declaring a struct instance:

    x := Person{Name: "Bob", Age: 29}
    y := Person{"Alice", 27}

    The latter, shorter, version requires that all field values are present, and in the proper order. By including an unexported field name (including with the blank identifier) in your struct definition, this short hand becomes impossible. Some people feel it is desirable to prevent this short-hand, and may do so with a blank field name, typically of type struct{}, which consumes no memory.

  • To make structs incomparable. As with the last example, a blank field name is not the only way to do this. Any struct element that is not comparable renders the entire struct incomparable. But this can be done with a blank field name as well. One example from the standard library is in the net/http package, where a blank field name of type [0]func() is used to render the struct incomparable (this works because functions are not comparable).

Do you know of any use for a blank field name in Go? Let me know!

Quotes from The Go Programming Language Specification Version of December 15, 2022

Share this

Related Content

Empty structs

We finally we have enough knowledge for the EBNF format not to seem completely foreign, so let’s jump back and take a look at that, with the examples provided in the spec… Struct types … StructType = "struct" "{" { FieldDecl ";" } "}" . FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] . EmbeddedField = [ "*" ] TypeName [ TypeArgs ] . Tag = string_lit . // An empty struct.

Struct tags

Struct types … A field declaration may be followed by an optional string literal tag, which becomes an attribute for all the fields in the corresponding field declaration. An empty tag string is equivalent to an absent tag. The tags are made visible through a reflection interface and take part in type identity for structs but are otherwise ignored. struct { x, y float64 "" // an empty tag string is like an absent tag name string "any string is permitted as a tag" _ [4]byte "ceci n'est pas un champ de structure" } // A struct corresponding to a TimeStamp protocol buffer.

Struct method promotion

Yesterday we saw an example of struct field promotion. But methods (which we haven’t really discussed yet) can also be promoted. Struct types … Given a struct type S and a named type T, promoted methods are included in the method set of the struct as follows: If S contains an embedded field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.