4 ms·
The last example is surprising: { await using { connection } = getConnection(); // Do stuff with connection } // Automatically closed! The objec
by codewiz 3y ago
The last example is surprising:
{
await using { connection } = getConnection();
// Do stuff with connection
} // Automatically closed!
The object returned by getConnection() is destructured to extract the field named connection and assign it to a local variable with the same name. The containing object presumably continues to exist anonymously so its Symbol.asyncDispose function will still be called when it goes out of scope.
Can anyone explain which language rule guarantees this behavior in C# and in TypeScript?
- oaiey 3y agoIf I understand your question right, for C# look at the following article, last paragraph regards object which leave the scope of the using (C# does not have the JS "var" but only the JS "let" behavior, so it is slightly different here). https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/using https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
- Merad 3y agoC# doesn't allow you to destructure a single property and as far as I can tell based on a quick test it doesn't allow you to combine a using declaration with destructuring. I agree that this is surprising syntax... as a C# dev my first interpretation of that code would be that connection is the thing being disposed since it's the variable being declared. I wonder if js/ts destructuring has always silently held a reference to the containing object or if they added that behavior to make this use case work?
- metaltyphoon 3y ago> C# doesn't allow you to destructure a single property and as far as I can tell https://sharplab.io/#v2:C4LgTgrgdgNAJiA1AHwAICYAMBYAUBgRj2NwDcBDMAAgAoB9GK8gcwFMBKKgXiqlYHcqABVZgAzgHsoNAEQBbVjJgFM7ANwlUBAJw0WHDbjxhWAYwlg4w0ZOlbMVAHLkFjAJZRgVAIJt1QA= https://sharplab.io/#v2:C4LgTgrgdgNAJiA1AHwAICYAMBYAUBgRj2Nw...
- Merad 3y agoYour code is destructuring two properties and discarding one of them. It doesn't work with a single property: https://sharplab.io/#v2:C4LgTgrgdgNAJiA1AHwAICYAMBYAUBgRj2NwDcBDMAAgAoBbAUwEoqBeKqBgdyoAUGwAZwD2UGgCJG4pgG4SqAgE56zObjxgGAY2Fg4fASLELMVAHLlGsoA= https://sharplab.io/#v2:C4LgTgrgdgNAJiA1AHwAICYAMBYAUBgRj2Nw... I think that records don't generate a deconstruct method when they only have one property, but even if you manually define one you'll get an error on `var (varName) = ...`
- zoogeny 3y agoI thought the same and I don't see motivation from it on the TC39 spec page [1] but I do see they have several examples that make use of the feature. It makes me realize I've never really considered the memory implications of doing something like: const { someSmallPart } = getSomeMassiveObject(); I don't really know the lifetime of "massive object". Probably a bad on my part for not knowing if Javascript garbage collection is allowed to notice that the only thing with a reference is `someSmallPart` and could therefore release any other parts of the massive object. If the answer to that is "no" then there is no problem with the above pattern. If the answer is "yes", then things could get complicated. e.g. async function getComplicatedObject() { const db = await db.getConnection( ... ); const f = await file.open( ... ); return { db, f, [Symbol.asyncDispose]: () => { db.close() f.close() } } { await using { f } = getComplicatedObject(); // ... more stuff with awaits where the GC might decide `db` isn't used so it can clean } // I would not expect a null ref error on `db` here when `asyncDispose` is called I mean, you can handle the above even if the GC is allowed to be smart and discard `db` - it just makes it significantly more complicated and requires clever book-keeping. 1. https://github.com/tc39/proposal-explicit-resource-management/ https://github.com/tc39/proposal-explicit-resource-managemen...
- wruza 3y agoIsn’t it natural that async dispose property would pin an entire object at `using` scope? If there’s no pin via using, then GC should be able to collect unused contents anytime.
- bakkoting 3y agoThe article is wrong. That's not going to be legal, specifically because it's confusing which object is being disposed.
- Dylan16807 3y agoI'm pretty sure that example won't work and is based on an old version. But assuming a javascript implementation where it works, I don't know what you mean by "language rule". The spec says what "using" means, and it doesn't require any behavior the language doesn't already have. The old spec said "When try using is parsed with an Expression, an implicit block-scoped binding is created for the result of the expression.", and the new one says "When a using declaration is parsed with BindingIdentifier Initializer, the bindings created in the declaration are tracked for disposal at the end of the containing Block or Module". Then the spec has an example implementation written in javascript, with the value being added to a (non-user-accessible) list. https://github.com/tc39/proposal-explicit-resource-management#using-declarations-with-explicit-local-bindings https://github.com/tc39/proposal-explicit-resource-managemen... Is that the language rule you wanted?