Joe Shaw
Joe Shaw

Reputation: 22592

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

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?

Upvotes: 3

Views: 1190

Answers (2)

Gustavo Niemeyer
Gustavo Niemeyer

Reputation: 22991

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.

Upvotes: 2

zzzz
zzzz

Reputation: 91203

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.

Upvotes: 3

Related Questions