I'm trying to build a simple orm layer for golang.
Which would take a struct and generate the cols []
which can then be passed to sql function
rows.Scan(cols...)
which takes pointers of fields in the struct corresponding to each of the columns it has found in the result set
Here is my example struct
type ExampleStruct struct {
ID int64 `sql:"id"`
aID string `sql:"a_id"`
UserID int64 `sql:"user_id"`
And this is my generic ORM function
func GetSqlColumnToFieldMap(model *ExampleStruct) map[string]interface{} {
typeOfModel := reflect.TypeOf(*model)
ValueOfModel := reflect.ValueOf(*model)
columnToDataPointerMap := make(map[string]interface{})
for i := 0; i < ValueOfModel.NumField(); i++ {
sql_column := typeOfModel.Field(i).Tag.Get("sql")
structValue := ValueOfModel.Field(i)
columnToDataPointerMap[sql_column] = structValue.Addr()
}
return columnToDataPointerMap
}
Once this method works fine i can use the map it generates to create an ordered list of sql pointers according to the column_names i get in rows() object
However i get below error on the .Addr()
method call
panic: reflect.Value.Addr of unaddressable value [recovered]
panic: reflect.Value.Addr of unaddressable value
Is it not possible to do this ? Also in an ideal scenario i would want the method to take an interface instead of *ExampleStruct so that it can be reused across different db models.
The error says the value whose address you want to get is unaddressable. This is because even though you pass a pointer to
GetSqlColumnToFieldMap()
, you immediately dereference it and work with a non-pointer value later on.This value is wrapped in an
interface{}
when passed toreflect.ValueOf()
, and values wrappped in interfaces are not addressable.You must not dereference the pointer, but instead use
Type.Elem()
andValue.Elem()
to get the element type and pointed value.Something like this:
With this simple change it works! And it doesn't depend on the parameter type, you may change it to
interface{}
and pass any struct pointers.Testing it:
Output (try it on the Go Playground):
Note that
Value.Addr()
returns the address wrapped in areflect.Value
. To "unwrap" the pointer, useValue.Interface()
:This will output (try it on the Go Playground):
For an in-depth introduction to reflection, please read blog post: The Laws of Reflection