重用结构以减少堆使用

Please explain what is happening in following code. The part I don't understand is the service struct. The service struct is a wrapper around the APIClient struct. When the NewAPIClient is called, it is using the service struct inside and copying onto itself. I cannot seem to wrap my head around this. Please advise and elaborate. Thank you.

type APIClient struct {
    cfg    *Configuration

    // Reuse a single struct instead of 
    // allocating one for each service on the heap.
    common service 

    // API Services
    AccountApi          *AccountApiService
    ContractApi         *ContractApiService
    FYIApi              *FYIApiService
    IBCustApi           *IBCustApiService
    MarketDataApi       *MarketDataApiService
    OrderApi            *OrderApiService
    PnLApi              *PnLApiService
    PortfolioApi        *PortfolioApiService
    PortfolioAnalystApi *PortfolioAnalystApiService
    ScannerApi          *ScannerApiService
    SessionApi          *SessionApiService
    TradesApi           *TradesApiService
}

type service struct {    
    client *APIClient    
}

func NewAPIClient(cfg *Configuration) *APIClient {    
    if cfg.HTTPClient == nil {    
            cfg.HTTPClient = http.DefaultClient    
    }    

    c := &APIClient{}    
    c.cfg = cfg    
    c.common.client = c    

    c.AccountApi = (*AccountApiService)(&c.common)    
    c.ContractApi = (*ContractApiService)(&c.common)
    c.FYIApi = (*FYIApiService)(&c.common)
    c.IBCustApi = (*IBCustApiService)(&c.common)
    c.MarketDataApi = (*MarketDataApiService)(&c.common)
    c.OrderApi = (*OrderApiService)(&c.common)
    c.PnLApi = (*PnLApiService)(&c.common)
    c.PortfolioApi = (*PortfolioApiService)(&c.common)
    c.PortfolioAnalystApi = (*PortfolioAnalystApiService)(&c.common)
    c.ScannerApi = (*ScannerApiService)(&c.common)
    c.SessionApi = (*SessionApiService)(&c.common)
    c.TradesApi = (*TradesApiService)(&c.common)

    return c
}

First, note that the expression (*AccountApiService)(&c.common) is an explicit conversion:

A conversion changes the type of an expression to the type specified by the conversion.

There is a number of rules that allow you to convert an expression of one type to another type, but the one relevant to your use case is this one:

  • ignoring struct tags (see below), x's type and T have identical underlying types.

So the assignment statement c.AccountApi = (*AccountApiService)(&c.common) can be explained like this: Since the field common is of type service the expression &c.common is of type *service, that type is then converted to *AccountApiService and the result of that conversion expression is then assigned to the c.AccountApi field.

And as we established above, for the conversion to work the two types, *AccountApiService and *service, must have the same underlying type. So, even though you haven't provided the definitions to any of those XxxApiService types, it is safe to assume that, if the code actually compiles, they are all defined as either

type XxxApiService service

or

type XxxApiService struct {
    client *APIClient
}

effectively giving them the same underlying type as the service's underlying type.


You may ask yourself why not initialize the various services like a normal person, why not write this

c.AccountApi = &AccountApiService{c}

instead? The answer to that is in the comment above the common field of APIClient.

Reuse a single struct instead of allocating one for each service on the heap.

Which means that instead of allocating a new instance of XxxApiService for each service field, the value allocated to common is shared by all of the service fields. The intent here, I assume, is resource/performance optimization.


And finally the assignment c.common.client = c. The APIClient has a field named common of type service, and the type service has a field named client of type *APIClient. This basically allows a client to hold a reference to itself through the common field, which, although it's not the primary aim here, is exactly what is happening in the example code. Basically recursion, an unintended feature of the APIClient design AFAICT, but it is there.