Go est un langage avec gestion automatique de la mémoire: le garbage collector, sans avoir besoin d'un programmeur, se charge de récupérer la mémoire utilisée par les objets qui ne sont plus utilisés par le programme. Mais toute l'automatisation dans son ensemble est limitée par la mémoire - nous devons encore nous occuper du reste des ressources utilisées par le programme nous-mêmes.

, - , 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
, . , ( ). Close , - .
. , 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
J'ai publié la bibliothèque KDone fournissant l'ensemble d'outils ci-dessus. Elle fait partie du projet Kata , qui sera discuté ensuite. Nous pouvons supposer qu'au moment où son API est stable et si elle change, elle sera insignifiante - néanmoins, la bibliothèque est encore fraîche et j'utilise toujours la version majeure zéro en cas de changements imprévus.
Un constructeur typique utilisant cette bibliothèque ressemble à ceci:
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
//
// .
}
Qu'est-ce que tu penses? Le concept est assez simple, mais peut-être ai-je manqué quelque chose dans mon raisonnement? Ou avez-vous des suggestions d'améliorations? Je serais heureux d'avoir des discussions dans les commentaires.