4 ms·
Re. "null protection": It is kind of hard to fit non-nullable pointers into Go. The fact that non-nullable pointers do not have a natural initial value and alwa
by xiaq 8y ago
Re. "null protection": It is kind of hard to fit non-nullable pointers into Go. The fact that non-nullable pointers do not have a natural initial value and always require an initial assignment is especially problematic in Go's struct semantics, because first of all, struct creation is supposed to be light-weight. If a struct contains pointers, the cheapest way to initialize it would be to assign them nil.
But the more interestingly problem is how Go's struct "constructor" works. It is very low-level, you just assign some fields in a struct like &SomeStruct{X: 1, Y: "y"}; you can also assign all fields, in which case you can omit the keys, like &SomeStruct{1, "y"}. let's call those the key-value variant and positional variant of "free-form constructor".
You can provide a "function constructor", which is simply a function that returns SomeStruct or * SomeStruct, but it is not required, nor can you forbid the free-form constructor.
This is in stark contrast with, say, Java, where the function constructor is both required and the only allowed form. That makes it easy to make sure that non-nullable pointer fields are always initialized, because the compiler only needs to check the constructors.
In Go it is much harder. You can make the free-form constructor check that non-nullable pointer fields get initial values, but then Go has this promise that the author of a struct can add new fields without breaking users of the free-form constructor, as long as they are using the key-value variant instead of the positional variant. So if a struct was originally:
type SomeStruct {
X int
Y string
}
A user can make an instance with &SomeStruct{X: 1, Y: "y"}. If the author of the struct decides to add a new field, the code should still work. That should be the case even if the new field is a non-nullable pointer field. This now defeats the purpose of non-nullable pointer fields.
- nicoburns 8y ago> but then Go has this promise that the author of a struct can add new fields without breaking users of the free-form constructor, as long as they are using the key-value variant instead of the positional varian This is the bit that would need to change. And actually, doing so would provide the same benefits that non-nullable types do in other contexts. Say a Go library user is using a struct in 5 places, and the library author adds a new field. The user updates the free-form constructor to properly initialise the new field in 4 of those 5 places, but overlooks the 5th. As it stands, that will fail silently if the program is relying on using a non-null value somewhere else in the code. If upgrading the library caused a compile time error then that bug would have been caught. Rust also has "free-form constructors" like Go, and this is the approach it takes: you must provide a value for all fields.
- giovannibajo1 8y agoGo has a pervasive concept of “default being zero” where people are encouraged to write code so that zero-initialization is valid and reasonable. This means that, when adding a new public field to a structure, you always make sure that the default value (nil for pointers) is valid and backward compatible with the previous behavior (eg no change in behavior). Rust has a half backed support for default initialization, where you can either mark all fields as default initialized via a macro or none (or you can mark them and add a constructor that only changes some, but then the API is confusing for clients unless default initialization is a valid construction). This means that it’s much more common to write constructors, and being forced to set many fields to 0, empty string, None, ecc. is tedious and factually useless; the provide support (Default macro) is not powerful enough to avoid this, unfortunately. I strongly prefer Go’s approach to default initialization in general, and I think it is orthogonal to non-nullable types (you could have both and save the day). For instance, I don’t see what we lose in Rust by saying that integers auto-initialize to zero unless you name them in the constructor.
- masklinn 8y ago> Rust has a half backed support for default initialization, where you can either mark all fields as default initialized via a macro or none (or you can mark them and add a constructor that only changes some, but then the API is confusing for clients unless default initialization is a valid construction). So it has half-baked support because… you can opt into exactly how Go behaves but it's not foisted upon you? > This means that it’s much more common to write constructors, and being forced to set many fields to 0, empty string, None, ecc. is tedious and factually useless; the provide support (Default macro) is not powerful enough to avoid this, unfortunately. Be the change you want to see in the world and open an RFC for partial defaulting? It's already possible to "fill in" literal struct with `..Default::default()`, if you can defend there being a use-case for it, this would be extendable to filling in default-able fields even without an explicit (and public) Default implementation for the struct. There was an issue on the tracker in the past (https://github.com/rust-lang/rust/issues/42692 https://github.com/rust-lang/rust/issues/42692) but the change is big enough that it would require an RFC, actual design and consensus, but that's the extent of it. Alternatively, build a macro doing that for you. There might already be one but a quick cargo search didn't turn anything up. > I strongly prefer Go’s approach to default initialization in general, and I think it is orthogonal to non-nullable types (you could have both and save the day). How can you have non-nullable fields with a zero value exactly? > For instance, I don’t see what we lose in Rust by saying that integers auto-initialize to zero unless you name them in the constructor. The regularity of the language to start with.
- masklinn 8y ago> first of all, struct creation is supposed to be light-weight. Non-nullable pointers don't make struct creation any more expensive. > You can provide a "function constructor", which is simply a function that returns SomeStruct or * SomeStruct, but it is not required, nor can you forbid the free-form constructor. You can have an exported function returning a non-exported type. This restricts the "freeform" constructor to the package's internal's. It severely limits the caller's ability to use it (unless you also provide an interface to work with), but it's there.