I'm new to Go and I'm trying to write a little program to save enumerated values to a database. The way I declare my values is as follows:
type FileType int64
const (
movie FileType = iota
music
book
etc
)
I use these values in my struct like this:
type File struct {
Name string
Type FileType
Size int64
}
I use gorp for my database stuff, but I guess the use of gorp isn't relevant to my problem. I put stuff in my DB like this:
dbmap.Insert(&File{"MyBook.pdf",movie,1000})
but when I try to retrieve stuff…
dbmap.Select(&dbFiles, "select * from Files")
I get the following error:
panic: reflect.Set: value of type int64 is not assignable to type main.FileType
When I use int64
as the type for the const(...)
and for the File.Type
field, everything works fine, but I'm new to Go and want to understand the problem. The way I see it, I have two problems:
I figured out, that one can implement the sql.Scanner
interface by implementing the following method:
Scan(src interface{}) error
I tried to implement the method and I even was able to get the right value from src
and convert it to a FileType
, but I was confused if I should implement the method for "(f *FileType)
or (f FileType)
. Either way the method gets invoked, however I'm not able to overwrite f
(or at least the update gets lost later) and the File
instances read from the DB always had a "0" as value for File.Type
.
Do you have any ideas on those two points?
I recently had the same need, and the solution is to implement two interfaces:
Here's a working example:
type FileType int64
func (u *FileType) Scan(value interface{}) error { *u = FileType(value.(int64)); return nil }
func (u FileType) Value() (driver.Value, error) { return int64(u), nil }
(f FileType)
is cheaper than (f *FileType)
for "native" types, pretty much unless you have a complex type, it's almost always better to not use a pointer.Slightly off-topic, but may be useful to others as I kept revisiting this question/answer when solving a similar problem when working with postgres enum fields in golang (which are returned as bytes).
// Status values
const (
incomplete Status = "incomplete"
complete Status = "complete"
reject Status = "reject"
)
type Status string
func (s *Status) Scan(value interface{}) error {
asBytes, ok := value.([]byte)
if !ok {
return errors.New("Scan source is not []byte")
}
*s = Status(string(asBytes))
return nil
}
func (s SubjectStatus) Value() (driver.Value, error) {
// validation would go here
return string(s), nil
}