Realizing why Go reflection restricts what struct fields can be modified
Recently I read Rust, reflection and access rules. Among
other things, it describes how a hypothetical Rust reflection system
couldn't safely allow access to private fields of things, and
especially how it couldn't allow code to set them through reflection.
My short paraphrase of the article's discussion is that in Rust,
private fields can be in use as part of invariants that allow unsafe
operations to be done safely through suitable public APIs. This
brought into clarity what had previously been a somewhat odd seeming
restriction in Go's reflect
package.
Famously (for people who've dabbled in reflect
), you can only set
exported struct fields. This is covered in both the Value.CanSet()
package documentation and
The Laws of Reflection (in
passing). Since one of the uses of reflection is for going between JSON
and structs, encoding/json only
works on exported struct fields and you'll find a lot of such fields in
lots of code. This requirement can be a bit annoying. Wouldn't it be
nice if you didn't have to make your fields public just to serialize
them easily?
(You can use encoding/json and still serialize non-exported struct fields, but you have to write some custom methods instead of just marking struct fields the way you could if they were exported.)
Go has this reflect
restriction, presumably, for the same
reason that reflection in Rust wouldn't be able to modify private
fields. Since private fields in a Go struct may be used by functions
and methods in the package to properly manage the struct, modifying
those fields yourself is unsafe (in the general sense). The
reflect
package will let you see the fields (and their values)
but not change their values. You're allowed to change exported
fields because (in theory) arbitrary Go code can already change the
value of those fields, and so code in the struct's package can't
count on them having any particular value. It can at least sort of
count on private fields having approved values (or the zero value,
I believe).
(I understand why the reflect
documentation doesn't explain
the logic of not being able to modify private fields, since package
documentation isn't necessarily the right place for a rationale.
Also, perhaps it was considered obvious.)
|
|