-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for Scan into pointer to RedisScan #418
Comments
The correct way to do this is actually: var foo Foo
_, err := redis.Scan(src, &foo) |
@stevenh sorry, my example probably wasn't sufficient for the problem I was trying to solve when I wrote this issue -- I was ultimately trying to scan into a pointer field of a struct and hoping to save some work with temporary variables or shims. An example of that structure would be something along these lines: type InnerStruct struct {
Foo int64
}
func (f *InnerStruct) RedisScan(src interface{}) error {
switch s := src.(type) {
case []byte:
f.Foo, _ = strconv.ParseInt(string(s), 10, 64)
case string:
f.Foo, _ = strconv.ParseInt(string(s), 10, 64)
default:
return fmt.Errorf("invalid type %T", src)
}
return nil
}
type OuterStruct struct {
Inner *InnerStruct
}
func TestUnit_Scan(t *testing.T) {
src := []interface{}{[]byte("1234"), nil}
var foo OuterStruct
_, err := redis.Scan(src, &foo.Inner) // ERROR
assert.NoError(t, err)
assert.Equal(t, OuterStruct{Inner: &InnerStruct{Foo: 1234}}, foo)
} To get around this, I made a little shim: func scanToPointer(t interface{}) interface{} {
return &scanShim{t}
}
type scanShim struct{ T interface{} }
func (s *scanShim) RedisScan(src interface{}) error {
if dest := reflect.ValueOf(s.T).Elem(); dest.Type().Kind() == reflect.Ptr && dest.CanInterface() {
if src == nil || src.([]byte) == nil {
// if our source is nil, leave the target unchanged (and presumably nil)
return nil
}
if dest.IsNil() {
dest.Set(reflect.New(dest.Type().Elem()))
}
if scanner, ok := dest.Interface().(redis.Scanner); ok {
return scanner.RedisScan(src)
}
}
return fmt.Errorf("type (%T) invalid for scanToPointer", s.T)
} Used like this: _, err := redis.Scan(src, scanToPointer(&foo.Inner)) (which works) |
This is what I would use in that scenario: type InnerStruct struct {
Foo int64
}
func (f *InnerStruct) RedisScan(src interface{}) (err error) {
switch s := src.(type) {
case []byte:
f.Foo, err = strconv.ParseInt(string(s), 10, 64) // <-- Added error check
case string:
f.Foo, err = strconv.ParseInt(s, 10, 64) // <-- Added error check
default:
return fmt.Errorf("invalid type %T", src)
}
return err // <-- Added error check
}
type OuterStruct struct {
Inner *InnerStruct
}
func TestUnit_Scan(t *testing.T) {
src := []interface{}{[]byte("1234"), nil}
foo := OuterStruct{&InnerStruct{}} // <-- Initialise the struct
_, err := redis.Scan(src, foo.Inner)
assert.NoError(t, err)
assert.Equal(t, OuterStruct{Inner: &InnerStruct{Foo: 1234}}, foo)
} |
(Thanks for taking the time, by the way) That's another reasonable solution, but leaves src2 := []interface{}{[]byte(nil), nil}
var bar OuterStruct
_, err = redis.Scan(src2, &bar.Inner)
assert.NoError(t, err)
assert.Equal(t, OuterStruct{}, bar) That is, using the |
Hi @bferullo could you test the above PR in your situation to make sure it addresses your issue? |
Totally does, thanks! |
Add support for scanning into a pointer to a type which supports RedisScan. Fixes #418
Currently, calling
redis.Scan
with a destination that is a pointer to a type that implementsredis.RedisScan
results in the following error:redigo.Scan: cannot assign to dest 0: cannot convert from Redis simple string to *data.Foo
Other flavors of Scan (e.g.
ScanStruct
) seem happy toreflect.New
a target if necessary. Can the same be extended toScan
?Example code:
go test
result:The text was updated successfully, but these errors were encountered: