How can I type select an interface on a pointer-to-pointer in Go?

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

How can I type select an interface on a pointer-to-pointer in 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) // 打印 **main.Foo

通常,这个函数会进行类型切换,然后对底层类型进行操作。我想为所有实现了我的Unmarshaler接口的类型编写通用代码,但是我无法弄清楚如何从特定类型的指向指针的指针转换到我的接口。

你不能在指向指针的指针上定义方法:

func (f **Foo) Unmarshal(data []byte) error {
    return json.Unmarshal(data, f)
}
// 编译错误:无效的接收器类型 **Foo(*Foo 是一个无名类型)

你也不能在指针类型上定义接收器方法:

type FooPtr *Foo

func (f *FooPtr) Unmarshal(data []byte) error {
    return json.Unmarshal(data, f)
}
// 编译错误:无效的接收器类型 FooPtr(FooPtr 是一个指针类型)

将其转换为Unmarshaler类型也不起作用:

x := target.(Unmarshaler)
// 报错:接口转换:**main.Foo 不是 main.Unmarshaler:缺少方法 Unmarshal

将其转换为*Unmarshaler也不起作用:

x := target.(*Unmarshaler)
// 报错:接口转换:接口是 **main.Foo,而不是 *main.Unmarshaler

如何在不需要针对每个可能的实现类型进行切换的情况下,从指向指针的指针类型转换为我的接口类型?

英文:

I have a pair of interfaces defined like so:

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

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

I have a simple type which implement these:

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)
}

I am using a library which defines a different interface, and implementing it like so:

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

The value being passed for target is a pointer to pointer:

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

Typically this function does a type switch and then operates on the type underneath. I would like to have common code for all types that implement my Unmarshaler interface but can't figure out how to get from a pointer-to-pointer of a specific type to my interface.

You cannot define methods on a pointer to a pointer:

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

You cannot define receiver methods on pointer types:

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)

Casting to Unmarshaler doesn't work:

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

Casting to *Unmarshaler doesn't work either:

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

How can I get from this pointer-to-pointer type to my interface type without needing to switch on every possible implementor type?

答案1

得分: 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()
}

输出结果为:

42

需要注意的是,上述代码中的 fmt.Println(*w.p) 实际上等同于 fmt.Println(*(*w).p),编译器会自动为你完成这个额外的工作。

英文:

It's ugly, but is is possible to have the semantic equivalent of a pointer to pointer receiver. For example:

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


Output:

42

Note that

fmt.Println(*w.p)

above is actually

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

where the extra work is done for you by the compiler automagically.

答案2

得分: 2

如果target是一个**Foo,而*Foo实现了Unmarshaler接口,你可以这样做:

var x Unmarshaler = *target

如果target是一个包含**Foointerface{},则可以使用以下方式:

var x Unmarshaler = *(target.(**Foo))

如果你使用类型切换,也是类似的思路。

每当我需要处理指向指针或引用的情况时,变量的生命周期通常很短,它们很快就会回到单个指针(或普通引用)的状态。

我会评估指向指针的确实是否在当前的使用场景中是必需的,或者是否可以做一些类似的操作。

英文:

If target is a **Foo and *Foo implements Unmarshaler, you can do:

var x Unmarshaler = *target

If target is a an interface{} containing a **Foo, this would work instead:

var x Unmarshaler = *(target.(**Foo))

It's a similar idea if you have a type switch.

Whenever I have to deal with pointer-to-pointer or pointer-to-reference, the variable life-time is generally pretty short and they quickly go back to being a single pointer (or plain reference) again.

I'd evaluate whether the pointer-to-pointer is indeed required for the use case at hand or whether you can do something like that.

huangapple
  • 本文由 发表于 2013年9月5日 00:23:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/18619089.html
匿名

发表评论

匿名网友

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

确定