In Go, is there any way to access private fields of a struct from another package? -
i have struct in 1 package has private fields:
package foo type foo struct { x int y *foo }
and package (for example, white-box testing package) needs access them:
package bar import "../foo" func change_foo(f *foo) { f.y = nil }
is there way declare bar
sort of "friend" package or other way able access foo.foo
's private members bar
, still keep them private other packages (perhaps in unsafe
)?
there is way read unexported members using reflect
func read_foo(f *foo) { v := reflect.valueof(*f) y := v.fieldbyname("y") fmt.println(y.interface()) }
however, trying use y.set, or otherwise set field reflect result in code panicking you're trying set unexported field outside package.
in short: unexported fields should unexported reason, if need alter them either put thing needs alter in same package, or expose/export safe way alter it.
that said, in interest of answering question, can this
func change_foo(f *foo) { // since structs organized in memory order, can advance pointer // field size until we're @ desired member. y, advance 8 // since it's size of int on 64-bit machine , int "x" first // in representation of foo. // // if wanted alter x, wouldn't advance pointer @ all, , // need convert ptrtof type (*int) ptrtof := unsafe.pointer(f) ptrtof = unsafe.pointer(uintptr(ptrtof) + uintptr(8)) // or 4, if 32-bit ptrtoy := (**foo)(ptrtof) *ptrtoy = nil // or *ptrtoy = &foo{} or whatever want }
this really, bad idea. it's not portable, if int ever changes in size fail, if ever rearrange order of fields in foo, change types, or sizes, or add new fields before pre-existing ones function merrily change new representation random gibberish data without telling you. think might break garbage collection block.
please, if need alter field outside package either write functionality change within package or export it.
edit: here's safer way it:
func change_foo(f *foo) { // note, doing reflect.valueof(*f) won't work, need pointerval := reflect.valueof(f) val := reflect.indirect(pointerval) member := val.fieldbyname("y") ptrtoy := unsafe.pointer(member.unsafeaddr()) realptrtoy := (**foo)(ptrtoy) *realptrtoy = nil // or &foo{} or whatever }
this safer, find correct named field, it's still unfriendly, slow, , i'm not sure if messes garbage collection. fail warn if you're doing weird (you make code little safer adding few checks, won't bother, gets gist across enough).
also keep in mind fieldbyname susceptible package developer changing name of variable. package developer, can tell have absolutely no qualms changing names of things users should unaware of. use field, you're susceptible developer changing order of fields no warning, have no qualms doing. keep in mind combination of reflect , unsafe is... unsafe, unlike normal name changes won't give compile time error. instead, program panic or weird , undefined because got wrong field, meaning if package developer did name change, still may not remember everywhere did trick , spend while tracking down why tests broke because compiler doesn't complain. did mention bad idea?
edit2: since mention white box testing, note if name file in directory <whatever>_test.go
won't compile unless use go test
, if want white box testing, @ top declare package <yourpackage>
give access unexported fields, , if want black box testing use package <yourpackage>_test
.
if need white box test 2 packages @ same time, however, think may stuck , may need rethink design.
Comments
Post a Comment