传递接口指针和赋值的值

huangapple go评论85阅读模式
英文:

pass interface pointer and assignment value

问题

我想在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, &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(&data)
	if err != nil {
		return err
	}

	//get v
	v = data.D
	fmt.Println(v)

	return nil
}

get函数传递了接口类型,我想改变其值,但是没有改变。
http://play.golang.org/p/wV7rBH028o

英文:

I want to write a file cache in Go. I am using gob encoding, and saving to a file, but my get function has some problem:

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, &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(&data)
if err != nil {
return err
}
//get v
v = data.D
fmt.Println(v)
return nil
}

The get function passes interface type and I want to change the value, but not change.
http://play.golang.org/p/wV7rBH028o

答案1

得分: 2

为了将一个未知值插入到类型为interface{}的变量v中,你需要使用反射。这有点复杂,但如果你想完全支持这个功能,你可以通过查看一些编码包(如jsongob)中的解码过程来了解如何实现。

以下是使用反射的基本版本的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(&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{}类型的值。由于在那个时候你会知道可能的类型,你可以使用类型断言来断定正确的值。

英文:

In order to insert an unknown value into v of type interface{}, you need to use reflection. This is somewhat involved, but if you want to support this in full, you can see how its done by walking through the decoding process in some of the encoding packages (json, gob).

To get you started, here's a basic version of your get function using reflection. This skips a number of checks, and will only decode something that was encoded as a pointer.

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(&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
}

I would actually suggest an easier way to handle this in your own code, which is to have the Get function return an interface{}. Since you will know what the possible types are at that point, you can use a type switch to assert the correct value.

答案2

得分: 1

另一种方法是直接从文件中返回值:

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(&data)
    if err != nil {
        return nil, err
    }

    fmt.Println(data.D)

    return data.D, nil
}

完整的工作示例:http://play.golang.org/p/178U_LVC5y

英文:

An alternative approach is to return directly the value from the file:

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(&data)
if err != nil {
return nil,err
}
fmt.Println(data.D)
return data.D,nil
}

full working example: http://play.golang.org/p/178U_LVC5y

huangapple
  • 本文由 发表于 2014年7月2日 00:27:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/24515156.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定