FAQ
Hi,

The current implementation of defer lets the deferred function runs when the owning function exits. While this is okay, I think it would be more useful if defer runs when it is out of scope (instead of functions). It would be useful in a scenario such as this :

func DoWork() error{

     for a:=0; a<10; a++{
          resource := AllocateResource()
          defer func(){
             //Clean up resource
          }
          //...
          if err:=resource.ErrorProne(); err!= nil{
              return error
          }
          //...
          if someWeirdCondition(){
              panic("Panicking")
          }
     }
}

The current implementation of defer does not let you do this. Instead you have to put the clean up codes at the end of the loop and at every possible early terminations from the loop.

Any thoughts?

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Search Discussions

  • peterGo at Jul 5, 2015 at 1:33 pm
    Henry,

    No.

    You can already do this:

    func doWork() error {
         resource := AllocateResource()
         defer func() {
             //Clean up resource
         }()
         //...
         if err := resource.ErrorProne(); err != nil {
             return error
         }
         //...
         if someWeirdCondition() {
             panic("Panicking")
         }
    }

    func DoWork() error {
         for a := 0; a < 10; a++ {
             err := doWork()
         }
    }

    Peter
    On Sunday, July 5, 2015 at 8:58:30 AM UTC-4, Henry Adi Sumarto wrote:

    Hi,

    The current implementation of defer lets the deferred function runs when
    the owning function exits. While this is okay, I think it would be more
    useful if defer runs when it is out of scope (instead of functions). It
    would be useful in a scenario such as this :

    func DoWork() error{

    for a:=0; a<10; a++{
    resource := AllocateResource()
    defer func(){
    //Clean up resource
    }
    //...
    if err:=resource.ErrorProne(); err!= nil{
    return error
    }
    //...
    if someWeirdCondition(){
    panic("Panicking")
    }
    }
    }

    The current implementation of defer does not let you do this. Instead you
    have to put the clean up codes at the end of the loop and at every possible
    early terminations from the loop.

    Any thoughts?
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henry Adi Sumarto at Jul 5, 2015 at 1:55 pm
    I am sure a workaround exists. I have thought of several variations as well. This is not a must-have feature where its absence will leave you stuck in a hopeless situation. In fact, you can also do away without defer if you wish and take defer out from Go. Defer itself is an ergonomic feature and not a critical one.

    However, I still think that defer should have run when it is out of scope instead of out of function. It just seem like a better design that way. I don't think that the change will break existing codes, considering that when you go out of function, you also go out of scope. Hence, defer in existing codes should still work.

    I hope the Go Team would seriously consider this.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henry Adi Sumarto at Jul 5, 2015 at 3:08 pm
    @Axel
    It's an unusual way to use defer. It looks like a conditional clean-up to me. If it is used like that, then yes it is going to be a breaking change.

    But adding an additional keyword for it looks like an ugly band-aid fix to me. Unless someone figure out something smart, if it were up to me, I would rather have a breaking change now than later when there are many more codes written in Go. Alternatively, we can leave it as is, but it's hard to ignore it knowing that there is a better way.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Jakob Borg at Jul 5, 2015 at 7:04 pm

    On 5 jul 2015, at 15:55, Henry Adi Sumarto wrote:

    I don't think that the change will break existing codes, considering that when you go out of function, you also go out of scope.
    I do this and variants a lot:

    func (t *T) Serve() {
         if debug {
             log.Println(t, "starting")
             defer log.Println(t, "exiting")
         }
         // stuff
    }

    There are a bunch of other cases where scope-scoped defer would break spectacularly.

    //jb

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henry Adi Sumarto at Jul 6, 2015 at 1:48 am
    For conditional defer, I prefer to do the following.

    resource := AllocateResource()
    defer func(){
          if conditionA {
          } else {
          }
    }()

    It seems clearer that way, and it is easier to notice when you are skimming the codes. Compare that to the one below.

    resource := AllocateResource()
    if conditionA {
        defer...
    }else{
        defer...
    }

    But everybody has their own ways of organizing their codes. So it is indeed possible that the change will break existing codes.

    Second, in Jakob's example, I don't think putting debugging code into the production code is a good idea, because you end up with different versions of the program. What works in the debug version may not work in the release version. Instead of embedding debug codes, I prefer using test. But everybody has their own ways of doing things.

    Lastly, what I don't like in a number of Go's workarounds is that it involves a liberal use of extra functions. When used properly, breaking down codes into functions can indeed improve readability. However, when you use too many of these, it hurts readability. In addition, calling a function has a cost. The cost may be small by today's standard, but when you put this inside a long loop, it may become significant, unless if Go compiler is smart enough to know which of these functions should be inlined. Compared to existing C/C++ compiler, Go compiler is still lagging behind when it comes to optimization.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Peter Kleiweg at Jul 5, 2015 at 4:20 pm
    You can use an embedded function

    http://play.golang.org/p/Xjy1g6W1Qf

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Nigel Tao at Jul 6, 2015 at 2:05 am

    On Sun, Jul 5, 2015 at 10:58 PM, Henry Adi Sumarto wrote:
    Any thoughts?
    The short answer is that we cannot change the meaning of "defer" for
    any Go 1.x release: https://golang.org/doc/go1compat

    The longer answer is that while there's benefit of a scope-scoped
    defer, there's also benefit in a function-scoped defer. This code:

    func foo(filename string) error {
       var r io.Reader
       if filename != "" {
         f, err := os.Open(filename)
         if err != nil {
           return err
         }
         defer f.Close()
         r = f
       } else {
         r = strings.NewReader(fakeInput)
       }
       // More code that reads from r.
       etc
    }

    is easier to write with a function-scoped defer than a scope-scoped defer.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henry Adi Sumarto at Jul 6, 2015 at 2:33 am
    @Nigel
    Actually, in the example you gave, scoped defer works better. The 'f' variable is local within the 'if'. In the scoped defer, 'f' will be cleaned up upon exiting the 'if'. It works as you intend it to be.

    With the function defer, 'f' will be out of scope and defer will execute when 'f' no longer exists.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henry Adi Sumarto at Jul 6, 2015 at 2:33 am
    @Nigel
    Actually, in the example you gave, scoped defer works better. The 'f' variable is local within the 'if'. In the scoped defer, 'f' will be cleaned up upon exiting the 'if'. It works as you intend it to be.

    With the function defer, 'f' will be out of scope and defer will execute when 'f' no longer exists.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henry Adi Sumarto at Jul 6, 2015 at 2:33 am
    @Nigel
    Actually, in the example you gave, scoped defer works better. The 'f' variable is local within the 'if'. In the scoped defer, 'f' will be cleaned up upon exiting the 'if'. It works as you intend it to be.

    With the function defer, 'f' will be out of scope and defer will execute when 'f' no longer exists.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henry Adi Sumarto at Jul 6, 2015 at 2:54 am
    @Nigel

    Sorry. I was replying through my cellphone and didn't read your code
    properly. I didn't notice the reassignment of 'f' to 'r'. But considering
    that r is the object being cleaned up, wouldn't it make more sense to move
    defer right below r?

    func foo(filename string) error{
           var r io.Reader
           defer func()[
                 //if r is *File, do this
                 //else do something else.
           }

          if filename !=""{
               //...
          }else{
              //...
          }
    }

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Josh Bleecher Snyder at Jul 6, 2015 at 3:23 am

    Sorry. I was replying through my cellphone and didn't read your code
    properly. I didn't notice the reassignment of 'f' to 'r'. But considering
    that r is the object being cleaned up, wouldn't it make more sense to move
    defer right below r?
    Putting the deferred close right next to the open, as Nigel did, makes
    the code obviously correct. Having the defer up at the declaration of
    r does not. And if you go to edit the code, you are likely to make the
    correct modifications, particularly if the function is long. Locality
    matters, particularly for humans.

    In addition, calling a function has a cost. The cost may be small by today's standard, but when you put this inside a long loop, it may become significant, unless if Go compiler is smart enough to know which of these functions should be inlined.
    The compiler does not currently inline any functions at all that
    include a defer. However, if you consider what is required for
    correctness--including having the correct state of memory and local
    variables, having the correct tracebacks, running multiple defers,
    etc.--I think you would find that any correct implementation of defer
    will not be made much cheaper by avoiding the function call overhead.


    Another consideration:

    Using function scoping for defers is general. You can get any behavior
    you want using a closure. However, some scopes are not accessible in
    the way you might want for your proposal. For example, consider:

    func f() {
        // ...
       func() {
         for i := 0; i < 3; i++ {
           x := use(i)
           defer cleanup(x)
         }
       }()
       // ...
    }

    This will clean up everything once the entire for loop completes. The
    relevant scope is the entire for loop (the scope at which i is
    declared). However, the scope of each call to defer is the scope of
    the body of the for loop (the scope at which x is declared). There is
    simply nowhere in the body of a for loop to put a defer that executes
    at the scope of the entire loop--and yet the calls you want to make to
    defer may depend on values calculated in the body of the loop.

    -josh

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Henry Adi Sumarto at Jul 6, 2015 at 2:33 am
    @Nigel
    Actually, in the example you gave, scoped defer works better. The 'f' variable is local within the 'if'. In the scoped defer, 'f' will be cleaned up upon exiting the 'if'. It works as you intend it to be.

    With the function defer, 'f' will be out of scope and defer will execute when 'f' no longer exists.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedJul 5, '15 at 12:58p
activeJul 6, '15 at 3:23a
posts14
users6
websitegolang.org

People

Translate

site design / logo © 2022 Grokbase