1

I know, similar questions have been asked, but I found no answer for that case:

type ExportedStruct struct{ //comes from a dependency, so I can't change it
  unexportedResource ExportedType
}

I want to call an exported method Close() on unexportedResource.

What I did was:

rs := reflect.ValueOf(myExportedStructPtr).Elem() //myExportedStructPtr is a pointer to an ExportedStruct object
resourceField := rs.FieldByName("unexportedResource")
closeMethod := resourceField.MethodByName("Close")
closeMethod.Call([]reflect.Value{reflect.ValueOf(context.Background())})

, which results in reflect.flag.mustBeExported using value obtained using unexported field.

This is quite annoying since I want to run more than one test which utilizes ExportedStruct, but I can't as long as the underlying resource is not used.

Since I can access private fields (as explained here) I have a bit hope that I'm allowed to access the public method of that field somehow, too. Maybe I'm just reflecting wrong?

NotX
  • 1,516
  • 1
  • 14
  • 28

3 Answers3

6

Unexported fields are for the declaring package only. Stop messing with them. They are not for you.

The linked answer can only access it by using package unsafe, which is not for everyday use. Package unsafe should come with a "not to touch" manual.

If you do need to access unexportedResource, make it exported. Either the field, or add a method to the type that calls unexportedResource.Close(). Or add a utility function to the package that does this (functions in the same package can access unexported fields and identifiers).

icza
  • 389,944
  • 63
  • 907
  • 827
  • Thanks so far. `ExportedStruct` is comes from a dependency, so I have not access to that. I'll add to the question. Regarding "not for you". It's not something I enjoy doing, and reflection in general is something I'ld love to avoid. But some API aren't just as well designed as they should be, especially in (neverending) v0-iterations. – NotX Dec 05 '19 at 13:56
  • 1
    @NotX Using reflection is fine, using `unsafe` is what you should avoid. But what you want is not possible with reflection, only with (also) using `unsafe`. – icza Dec 05 '19 at 14:10
4

While @icza's answer gives you reason why you should not do it, here is a way of how to do it using reflect and unsafe:

var t pkg.T
v := reflect.ValueOf(&t).Elem()
f := v.FieldByName("t")
rf := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem()
rf.MethodByName("Print").Call(nil)

playground: https://play.golang.org/p/CmG9e4Bl9gg

leaf bebop
  • 7,643
  • 2
  • 17
  • 27
1

I am afraid that what you are trying to do is impossible through reflection.

Below is the implementation of reflect.Call:

func (v Value) Call(in []Value) []Value {
    v.mustBe(Func)
    v.mustBeExported()
    return v.call("Call", in)
}

As you can see there is an explicit check (i.e. mustBeExported()) if Value was obtained from an exported field or not.

Typically there is a reason why fields are not exported. If you want to manipulate that field you will have to use methods implemented by the ExportedStruct struct.

If you can modify the code where ExportedStruct is defined, you can easily implement a wrapper Close method on that. For example:

type ExportedStruct struct{
  unexportedResource ExportedType
}

func (e ExportedStruct) Close(){
  e.unexportedResource.Close()
}
Giulio Micheloni
  • 1,290
  • 11
  • 25
  • 1
    You're right, `func (v Value) Call(in []Value) []Value` was exactly where I ended up in. I forgot to mention that I can't modify the place `ExportedStruct` came from. I've added that. Thanks! – NotX Dec 05 '19 at 14:03