kmad1729
kmad1729

Reputation: 1614

Effectively wrapping public sdk types in golang

I am using pagerduty go sdk to do a bunch of api requests. Particularly I am making use of

func NewClient(authToken string) *Client

to create a new Client type. I want to add some utility functions to my own work to *Client. I tried doing this:

type BetterPdClient *pagerduty.Client

func NewClient(auth string) BetterPdClient {
    return pagerduty.NewClient(auth)

}

func (b *BetterPdClient) DoSomething() {
    b.GetIncident(....)
}

func main() {
    pd_client := NewClient("")
    fmt.Println(pd_client)
    pd_client.DoSomething()
}

But I get the following error:

invalid receiver type *BetterPdClient (BetterPdClient is a pointer type) 

I understand that DoSomething() is expecting a pointer as caller. Only other way I could think of is sending the ptr as a function argument:

func NewClient(auth string) *pagerduty.Client { 
    return pagerduty.NewClient(auth) 

} 

func DoSomething(cl *pagerduty.Client) { 
    fmt.Println(cl) 
} 

func main() { 
    pd_client := NewClient("") 
    fmt.Println(pd_client) 
    DoSomething(pd_client) 
} 

Is there a better way?

Upvotes: 0

Views: 189

Answers (1)

mkopriva
mkopriva

Reputation: 38258

Declaring a type as a pointer to another type is almost never what you want because Go doesn't allow you to add methods to that new type, nor to the pointer of that type as you've already figured out yourself. This one doesn't compile either:

type T struct{}

type P *T

func (P) M() {}

If you want to "extend" a type without "hiding" it's existing functionality your best bet is to embed it in a struct.

type T struct{
    // ...
}

func (T) M() {}

type U struct {
    *T
}

func NewU() *U {
    return &U{&T{}}
}

func (U) N() {}

func main() {
    u := NewU()

    u.M()
    u.N()
}

And what I mean by "hiding existing functionality" is that when you define a new type in terms of another, already existing type, your new type will not get direct access to the methods of the existing type. All you're doing is just saying that your new type should have the same structure as the already existing type. Although it's worth pointing out that this property gives you the ability to convert one type to the other...

type T struct{
    // ...
}

func (T) M() {}

type U T

func NewU() *U {
    return &U{}
}

func (U) N() {}

func main() {
    u := NewU()

    u.M() // compile error
    u.N()

    // convert *U to *T and then call M
    (*T)(u).M()
}

Upvotes: 1

Related Questions