英文:
How to make an addressable getter that casts from an interface
问题
我有一个名为Context
的概念,它是一个可以容纳任何结构的映射。基本上,我想创建一个通用的getter函数,类似于JSON解码的方式,可以“填充”目标接口。
以下是我希望它工作的示例:
type Context map[string]interface{}
// 将保存在上下文中的随机结构体
type Step struct {
Name string
}
func main() {
stepA := &Step{Name: "Cool Name"}
c := Context{}
c["stepA"] = stepA
var stepB *Step
err := c.Get("stepA", &stepB)
if err != nil {
panic(err)
}
fmt.Println(stepB.Name) // Cool Name
stepB.Name = "CoolName2"
fmt.Println(stepA.Name) // 我希望输出: CoolName2
}
func (c Context) Get(stepId string, dest interface{}) error {
context, ok := c[stepId]
if !ok {
return nil
}
destinationValue := reflect.ValueOf(dest)
contextValue := reflect.ValueOf(context)
destinationValue.Elem().Set(contextValue.Elem()) // 这里会报错
return nil
}
我倾向于使用反射(reflect),但也许我不需要它?所以我对其他建议持开放态度(除了泛型,因为那会使其他事情变得复杂)。我在上述代码中遇到了以下错误:
panic: reflect: reflect.Value.Set using unaddressable value
你可以在这里进行测试。
英文:
I have the concept of Context
which is a map that can hold any structure. Basically, I want to create a generic getter that adddressably 'populates' the destination interface (similarly to how json decoding works).
Here's an example of how I want this to work:
type Context map[string]interface{}
// Random struct that will be saved in the context
type Step struct {
Name string
}
func main() {
stepA := &Step{Name: "Cool Name"}
c := Context{}
c["stepA"] = stepA
var stepB *Step
err := c.Get("stepA", stepB)
if err != nil {
panic(err)
}
fmt.Println(stepB.Name) // Cool Name
stepB.Name = "CoolName2"
fmt.Println(stepA.Name) // I want to say: CoolName2
}
func (c Context) Get(stepId string, dest interface{}) error {
context, ok := c[stepId]
if !ok {
return nil
}
destinationValue := reflect.ValueOf(dest)
contextValue := reflect.ValueOf(context)
destinationValue.Set(contextValue) // Errors here
return nil
}
I leaned towards using reflect, but maybe I don't need it? - so opened to other suggestions (except for generics as that complicates other matters) I'm getting the following error with the above:
panic: reflect: reflect.Value.Set using unaddressable value
You can test it here.
答案1
得分: 1
传递给Get方法的参数必须是指针类型,其元素类型与上下文映射中的类型相同。因此,如果上下文映射中的值的类型是*Step
,那么参数的类型必须是**Step
。此外,传入的参数不能为nil
,它可以是指向nil
的指针,但它本身不能是nil
。
所以在你的情况下,你应该这样做:
var stepB *Step
err := c.Get("stepA", &stepB) // 传递指针的指针
if err != nil {
panic(err)
}
并且稍作修改的Get
方法如下:
func (c Context) Get(stepId string, dest interface{}) error {
context, ok := c[stepId]
if !ok {
return nil
}
dv := reflect.ValueOf(dest)
if dv.Kind() != reflect.Ptr || dv.IsNil() {
return errors.New("dest must be non-nil pointer")
}
dv = dv.Elem()
cv := reflect.ValueOf(context)
if dv.Type() != cv.Type() {
return errors.New("dest type does not match context value type")
}
dv.Set(cv)
return nil
}
链接:https://go.dev/play/p/OECttqp1aVg
英文:
The argument passed to Get must be a pointer type whose element type is identical to the type in the context map. So if the value in the context map is of type *Step
, then the argument's type must be **Step
. Also the passed in argument cannot be nil
, it can be a pointer to nil
, but it itself cannot be nil
.
So in your case you should do:
var stepB *Step
err := c.Get("stepA", &stepB) // pass pointer-to-pointer
if err != nil {
panic(err)
}
And the Get
method, fixed up a bit:
func (c Context) Get(stepId string, dest interface{}) error {
context, ok := c[stepId]
if !ok {
return nil
}
dv := reflect.ValueOf(dest)
if dv.Kind() != reflect.Ptr || dv.IsNil() {
return errors.New("dest must be non-nil pointer")
}
dv = dv.Elem()
cv := reflect.ValueOf(context)
if dv.Type() != cv.Type() {
return errors.New("dest type does not match context value type")
}
dv.Set(cv)
return nil
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论