How does unkeyed literals prevention work

Viewed 444

Per this doc

type Point struct {
    X, Y float64
    _    struct{} // to prevent unkeyed literals
}

For Point{X: 1, Y: 1} everything will be fine, but for Point{1,1} you will get a compile error:

./file.go:1:11: too few values in &Pointer literal

Then I tried it in another data type _ byte and _ func() as below

type Pointer struct {
    X, Y int
    //_ byte   // to prevent unkeyed literals
    //_ func() // to prevent unkeyed literals
}

Both of them could prevent unkeyed literals. How does it prevent unkeyed literal? Is _ struct{} more efficient?

2 Answers

Unkeyed structs require you to specify all struct keys; it is an error if you don't specify the value for Y for example:

type Point struct {
    X, Y float64
}

_ = Point{1}

// Output:
// ./main.go:8:8: too few values in Point{...}

The _ struct{} field doesn't really prevent unkeyed literals from the same package, as you can still do:

type Point struct {
    X, Y float64
    _    struct{} // to prevent unkeyed literals
}

_ = Point{1, 2, struct{}{}}
// Ugly and weird, but valid!

But in order to be able to assign values in struct fields from other packages they need to be "exported", that is, start with a capital letter, and _ doesn't, so this is an error:

_ = x.Point{1, 2, struct{}{}}

// Output
// ./main.go:6:28: implicit assignment of unexported field '_' in x.Point literal

There is nothing special about _; you can use anything else that doesn't start with a capital as well, such as noexport struct{} or whatnot.


Why struct{} and not byte or int? Well, those types allocate some amount of memory; for an int it's usually 8 bytes (or 4 bytes on a 32bit system), and byte is an alias for uint8 and allocates one byte.

struct{} on the other hand is an "empty" type (you can't assign anything to it) and won't use any memory. This is a very small optimisation, but if you're going to type something you might as well type struct{}.


Is all of this worth it? In my opinion it's not; if someone wants to use unkeyed struct literals with your library then that's their choice. Many lint tools will already warn on this, including the built-in go vet:

$ go vet main.go
./main.go:8:6: net/mail.Address composite literal uses unkeyed fields

How does it prevent unkeys literals works?

An unkeyed struct literal must specify all fields; by adding a field that cannot be specified from outside the package, it makes it impossible to use this format, so it requires a keyed literal. "Keyed" or "unkeyed" refers to whether the field names appear in the struct literal.

Does _ struct{} more efficient?

Yes, because it has a width of zero, so it doesn't consume any memory. All other types would increase the memory footprint of the struct unnecessarily.

Related