передать указатель интерфейса и значение присвоения

#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