Go é uma linguagem com gerenciamento automático de memória: o garbage collector, sem a necessidade de um programador, se encarrega de recuperar a memória utilizada pelos objetos não mais utilizados pelo programa. Mas toda a automação como um todo é limitada pela memória - ainda precisamos cuidar do resto dos recursos usados pelo programa.
, - , runtime.SetFinalizer. , , . Go - , , , , .
res1, err := NewResource1()
if err != nil {
return nil, err
}
res2, err := NewResource2(res1)
if err != nil {
res1.Close()
return nil, err
}
res3, err := NewResource3(res2)
if err != nil {
res2.Close()
res1.Close()
return nil, err
}
v, err := res3.DoSomething()
if err != nil {
res3.Close()
res2.Close()
res1.Close()
return nil, err
}
res3.Close()
res2.Close()
res1.Close()
return v, nil
. , C# Java using statement try-with-resources statement . Go , , defer statement. , :
res1, err := NewResource1()
if err != nil {
return nil, err
}
defer res1.Close()
res2, err := NewResource2(res1)
if err != nil {
return nil, err
}
defer res2.Close()
res3, err := NewResource3(res2)
if err != nil {
return nil, err
}
defer res3.Close()
return res3.DoSomething()
Close . Close - .
, . - defer . , , - , . , , . , , - , - - , (, main), , , , , .
, Wire. ( Wire) . . cleanup function, . .
Dedicated finalization
, c cleanup function, Wire, . , Close ( ) :
;
.
, , , . , Go :
res, cleanup, err := NewResource()
if err != nil {
return err
}
// cleanup, .
if err := res.DoSomething(); err != nil {
return err
}
, (, ) ( ) . , " ", , , "" "" , .
Composite finalization
defer (, ), :
func Finalize(finalizers ...func()) {
// .
for i := len(finalizers) - 1; i >= 0; i-- {
func() {
defer func() {
// , .
// multierror :
// 1) ;
// 2) .
recover()
}()
finalizers[i]()
}()
}
}
func NewResource3() (*Resource3, func(), error) {
var (
finalizers []func() //
successful bool //
)
defer func() {
// ,
// -
// .
if !successfull {
Finalize(finalizers...)
}
}()
res1, fin1, err := NewResource1()
if err != nil {
return nil, nil, err
}
finalizers = append(finalizers, fin1)
res2, fin2, err := NewResource2(res1)
if err != nil {
return nil, nil, err
}
finalizers = append(finalizers, fin2)
res3 := &Resource3{
resource2: res2,
}
fin3 := func() {
Finalize(finalizers...)
}
// .
//
// .
successful = true
return res3, fin3, nil
}
Finalize - .
new - error - defer , , , , .
KDone
Publiquei a biblioteca KDone com o conjunto de ferramentas acima. Ela faz parte do projeto Kata , que será discutido a seguir. Podemos supor que no momento sua API está estável e se mudar, será insignificante - no entanto, a biblioteca ainda está fresca e eu ainda uso a versão principal zero no caso de mudanças imprevistas.
Um construtor típico que usa esta biblioteca tem a seguinte aparência:
func NewResource(...) (res Resource, dtor kdone.Destructor, err error) {
defer kerror.Catch(&err) //
// KError.
// .
reaper := kdone.NewReaper() // reaper.
defer reaper.MustFinalize() //
// .
// ... reaper.MustAssume(dtor) ... // reaper
//
// .
return res, reaper.MustRelease(), nil // reaper
//
// .
}
O que você acha? O conceito é bastante simples, mas talvez eu tenha perdido algo em meu raciocínio? Ou você tem sugestões de melhorias? Eu ficaria feliz em ter discussões nos comentários.