Hi there!
So I recently began programming an IoC implementation and had the idea to use channels as sort-of asynchronous getting of elements. But this requires the user to sync back with the container (otherwise the container does not know when the client finished whatever business he wants to to with the given element).
Okay, so what I came up with is to write a wrapper around this channel and - using reflection - to set the value as soon as it is ready. So here is my current code:
// Some boring type checks and error checks have been removed
func (cont *IoCContainer) Get(i interface{}) interface{} {
val := reflect.ValueOf(i)
// Here I do the "hidden magic", getting a channel that fires
// with the wanted element once the container was initialized.
ch := cont.GetChannel(i)
// This is where I get a new pointer to the element type (this is what we
// will return)
ret := reflect.New(val.Type().Elem())
go func() {
// Now we wait for the container to do his stuff
got := reflect.ValueOf(<-ch)
// Call a helper method, to "cast" to the correct type
// (eg. we want *MyStruct, but we got a **MyStruct)
wanted := getNormalizedValue(val.Type(), got)
// Set the element value, so that references to
ret.Elem().Set(wanted.Elem())
// Notify the container, that we did what we have to do and that
// the next chan can be filled
ch <- nil
}()
return ret.Interface()
}
So this works quite well. Whenever I call this function with a resolvable (pointer!) type, it first returns garbage, and later, after IoCContainer.Init()
is called, it will publish to the channel and sets the value.
But there is a catch. The immediately returned value isn’t a nil pointer, but a pointer to a zero value. This means, that if a careless client calls a function on the returned value before calling Init
on the container, its basically impossible to distinguish if the Container returned a value, that happens to be the zero value, or if the container isn’t initialized and the function was called too early.
So finally, after all this rambling (sorry for that), here is my question:
How can I return a nil pointer, and later set it to the the real value?
Here are some of the things I tried, but failed:
// At initialization
ret := reflect.New(val.Type())
// in go routine
ret.Elem().Set(wanted)
// at return statement
return ret.Elem().Interface()
// Fails because even after Init the value is still nil of the got value
// Using reflect.Zero instead of reflect.New
ret := reflect.Zero(val.Type())
ret.Set(wanted) // panic: reflect: reflect.Value.Set using unaddressable value
ret.Interface()
// Using Zero with with ret.Elem()
ret := reflect.Zero(val.Type())
ret.Elem().Set(wanted.Elem()) // panic: reflect: call of reflect.Value.Set on zero Value
ret.Interface()
// Using an actual interface{} instead of reflect value
// A initialization
temp := reflect.New(val.Type()).Elem().Interface()
ret := &temp
// In go routine
*ret = wanted.Interface{} // Value does not get updated (remains nil)
// at return statement
return *ret