Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is a good proposal, but I'm worried about the choice of syntax. Simply swapping `const x = ...` for `using x = ...` feels like it's not a "weighty" enough piece of syntax for such a complicated automatic disposal feature. I understand that the idea is to make it very low-effort and easily applicable to multiple resources, but I think intuitively I wouldn't exist "using x =" to call any sort of complex code "automatically". Instead, I think the block-scoped proposal in the "withdrawn" section makes a lot more sense

    using (x = ...) {
      console.log(x.whatever)
    }
Thoughts? I feel like the current proposal has a lot of the same issues I dislike with C++ (a focus on "terser" syntax obscures what is actually happening e.g. initializer lists don't actually tell you what constructor they're calling).


They took the syntax (almost) straight from C# where it works great. We had only block-scoped using for a long time and that sucked; the variable declaration style is definitely better and we readily adopted it when it became available.


C# had only block based using for a long time. It made a lot of code unreadable because the using statements introduced a ton clutter. The newer syntax is way better.


It seems fine, the increased scoping would just be bothersome.

The annoyance to me is that like all scope / defer constructs it gets wonky when paths split between a success and a failure path, and you do want the resource to escape in the former case. That's an area where RAII really shines.


> The annoyance to me is that like all scope / defer constructs it gets wonky when paths split between a success and a failure path, and you do want the resource to escape in the former case. That's an area where RAII really shines.

Is this even supported by the proposal in question though? I don't see anything here that indicates any sort of escape analysis or object tracking is performed—resources are always cleaned up lexically so `using foo = handle(); return foo` will always call foo to be cleaned up, just like the equivalent `finally` statement.


That's correct, there is no escape analysis or anything like it. Though there is a way to handle the "I want to return a resource" case: the DisposableStack class. You can call `stack.move()` to "move" ownership of a resource (and rely on the place you're passing it to to do cleanup). E.g.

  using stack = new DisposableStack();
  stack.use(resource());
  if (condition) {
    // the resources will _not_ get disposed automatically by this return
    // it's the caller's responsibility to call `stack.dispose()`
    return stack.move();
  }


> Is this even supported by the proposal in question though?

No and that’s exactly the problem.

If you “using” a resource and you leak it, you’ve now handed off a disposed off and probably useless resource.

If you don’t, and don’t, you have a resource leak.

In trivial cases this is fine-ish, just do whichever is needed, but when there are conditional paths internally (sometimes you signal an error others you return normally) you can’t “using” at all, you’re back to try/except and disposing by hand.

RAII does not have that issue, it does the right thing every time.

A few languages solve this by having additional constructs for these specific scenario (e.g. errdefer), though that requires more direct integration with the language’s error reporting, and thus forbids (or at least does not work with) alternate ones.


I toyed with this syntax a few years ago: https://gist.github.com/davidmurdoch/dc37781b0200a2892577363...


The forced nesting that particular proposal makes it less ergonomic (especially for scopes with multiple such resources), and would bring very little benefit over a simple library-based approach (instead of a language feature), something like the following for your example:

    using(..., x => {
      console.log(x.whatever)
    });


I don't see anything to suggest "using" bindings can't be closed over just as with any other lexical binding. Indeed, your example does so. Nothing in the proposal would seem to prevent returning such a closure from a function that acquires a resource, or a collection of same, and being able to comfortably use the bound resources without losing the guarantee they'll be properly disposed once no longer in scope.

Granted it still could get screwed up. But if it doesn't, and assuming of course that I've understood it correctly to begin with, I suspect a comfortable way to think about it will be as a means of hinting to the GC how it should handle cases where free() alone doesn't suffice.


I think you two are actually on the same page— their critique is of the nested block version of the proposal that OP prefers, not the the actual stage 3 proposal.


`with` blocks in Python become very ugly when nested, I'm glad it's a single line personally




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: