Get with:
go get github.com/nguyengg/init-onceinit.Once makes sure the initialiser is called exactly once regardless of its return status:
package main
import (
"context"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
ini "github.com/nguyengg/init-once"
)
type App struct {
Client *s3.Client
once ini.Once
}
func (a *App) DoSomething(ctx context.Context) error {
// because ini.Once is used, subsequent a.once.Do will always return the same error, nil or non-nil.
if err := a.once.Do(func() error {
return a.init(ctx)
}); err != nil {
return err
}
// there's no risk of a nil Client pointer here due to the err check above.
// sync.Once with an extra initErr or sync.OnceValue would have been needed to achieve the same thing.
_, _ = a.Client.GetObject(ctx, &s3.GetObjectInput{})
return nil
}
func (a *App) init(ctx context.Context) error {
if a.Client == nil {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return err
}
a.Client = s3.NewFromConfig(cfg)
}
return nil
}If you want to be able to retry initilisation over and over until first success, use init.SuccessOnce:
package main
import (
"context"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
ini "github.com/nguyengg/init-once"
)
type App struct {
Client *s3.Client
once ini.SuccessOnce
}
func (a *App) DoSomething(ctx context.Context) error {
// because ini.SuccessOnce is used, ini can be retried until its first success!
if err := a.once.Do(func() error {
return a.init(ctx)
}); err != nil {
return err
}
// there's still no risk of a nil Client pointer here due to the err check above.
_, _ = a.Client.GetObject(ctx, &s3.GetObjectInput{})
return nil
}
func (a *App) init(ctx context.Context) error {
if a.Client == nil {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return err
}
a.Client = s3.NewFromConfig(cfg)
}
return nil
}