#go
#Вперед
Вопрос:
Я хочу записать файловый кэш в Go. Я использую кодировку gob и сохраняю в файл, но у моей get
функции есть некоторые проблемы:
package main
import (
"encoding/gob"
"fmt"
"os"
)
var (
file = "tmp.txt"
)
type Data struct {
Expire int64
D interface{}
}
type User struct {
Id int
Name string
}
func main() {
user := User{
Id: 1,
Name: "lei",
}
err := set(file, user, 10)
if err != nil {
fmt.Println(err)
return
}
user = User{}
err = get(file, amp;user)
if err != nil {
fmt.Println(err)
return
}
//user not change.
fmt.Println(user)
}
func set(file string, v interface{}, expire int64) error {
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer f.Close()
//wrapper data
//save v in data.D
data := Data{
Expire: expire,
D: v,
}
gob.Register(v)
enc := gob.NewEncoder(f)
err = enc.Encode(data)
if err != nil {
return err
}
return nil
}
func get(file string, v interface{}) error {
f, err := os.OpenFile(file, os.O_RDONLY, 0600)
if err != nil {
return err
}
defer f.Close()
var data Data
dec := gob.NewDecoder(f)
err = dec.Decode(amp;data)
if err != nil {
return err
}
//get v
v = data.D
fmt.Println(v)
return nil
}
get
Функция передает тип интерфейса, и я хочу изменить значение, но не изменить.
http://play.golang.org/p/wV7rBH028o
Ответ №1:
Для того, чтобы вставить неизвестное значение в v
тип interface{}
, вам необходимо использовать отражение. Это несколько сложно, но если вы хотите поддерживать это в полном объеме, вы можете увидеть, как это делается, пройдя через процесс декодирования в некоторых пакетах кодирования ( json
, gob
).
Чтобы вы начали, вот базовая версия вашей get
функции, использующая отражение. Это пропускает ряд проверок и будет декодировать только то, что было закодировано как указатель.
func get(file string, v interface{}) error {
f, err := os.OpenFile(file, os.O_RDONLY, 0600)
if err != nil {
return err
}
defer f.Close()
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
panic("need a non nil pointer")
}
var data Data
dec := gob.NewDecoder(f)
err = dec.Decode(amp;data)
if err != nil {
return err
}
dv := reflect.ValueOf(data.D)
if dv.Kind() != reflect.Ptr {
panic("didn't decode a pointer")
}
rv.Elem().Set(dv.Elem())
return nil
}
На самом деле я бы предложил более простой способ справиться с этим в вашем собственном коде, который заключается в том, чтобы Get
функция возвращала interface{}
. Поскольку на этом этапе вы будете знать, какие возможны типы, вы можете использовать переключатель типа для утверждения правильного значения.
Ответ №2:
Альтернативный подход заключается в возврате значения непосредственно из файла:
func get(file string) (interface{}, error) {
f, err := os.OpenFile(file, os.O_RDONLY, 0600)
if err != nil {
return nil, err
}
defer f.Close()
var data Data
dec := gob.NewDecoder(f)
err = dec.Decode(amp;data)
if err != nil {
return nil,err
}
fmt.Println(data.D)
return data.D,nil
}
полный рабочий пример:http://play.golang.org/p/178U_LVC5y