如何在Go中的指针指针上键入选择接口?

时间:2013-09-04 16:23:30

标签: pointers types interface casting go

我有一对如此定义的接口:

type Marshaler interface {
    Marshal() ([]byte, error)
}

type Unmarshaler interface {
    Unmarshal([]byte) error
}

我有一个简单的类型来实现这些:

type Foo struct{}

func (f *Foo) Marshal() ([]byte, error) {
    return json.Marshal(f)
}

func (f *Foo) Unmarshal(data []byte) error {
    return json.Unmarshal(data, &f)
}

我正在使用一个定义不同接口的库,并按如下方式实现它:

func FromDb(target interface{}) { ... }

target传递的值是指向指针的指针:

fmt.Println("%T\n", target) // Prints **main.Foo

通常,此功能执行类型切换,然后对下面的类型进行操作。我希望为实现我的Unmarshaler接口的所有类型都有通用代码,但是无法弄清楚如何从特定类型的指针指向我的接口。

您无法在指向指针的指针上定义方法:

func (f **Foo) Unmarshal(data []byte) error {
    return json.Unmarshal(data, f)
}
// compile error: invalid receiver type **Foo (*Foo is an unnamed type)

您无法在指针类型上定义接收方法:

type FooPtr *Foo

func (f *FooPtr) Unmarshal(data []byte) error {
    return json.Unmarshal(data, f)
}
// compile error: invalid receiver type FooPtr (FooPtr is a pointer type)

转换为Unmarshaler不起作用:

x := target.(Unmarshaler)
// panic: interface conversion: **main.Foo is not main.Unmarshaler: missing method Unmarshal

投射到*Unmarshaler也不起作用:

x := target.(*Unmarshaler)
// panic: interface conversion: interface is **main.Foo, not *main.Unmarshaler

如何从指针到指针类型转换到我的接口类型而无需打开每个可能的实现者类型?

2 个答案:

答案 0 :(得分:3)

这很难看,但是 可能具有指向指针接收器的指针的语义等价物。例如:

package main

import "fmt"

type P *int

type W struct{ p P }

func (w *W) foo() {
        fmt.Println(*w.p)
}

func main() {
        var p P = new(int)
        *p = 42
        w := W{p}
        w.foo()
}

Playground


输出:

42

请注意

fmt.Println(*w.p)
上面的

实际上是

fmt.Println(*(*w).p)

编译器自动为您完成额外的工作。

答案 1 :(得分:2)

如果target**Foo*Foo实施Unmarshaler,您可以执行以下操作:

var x Unmarshaler = *target

如果目标是包含interface{}的{​​{1}},则可以这样做:

**Foo

如果你有一个类型开关,这是一个类似的想法。

每当我必须处理指向指针或指向引用的指针时,变量的生命周期通常很短,并且它们很快又会再次成为单个指针(或普通引用)。

我会评估手头的用例是否确实需要指针指针,或者你是否可以做类似的事情。