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.