A couple of extensions to `with` that would be worthwhile

Paul Backus snarwin at gmail.com
Wed Oct 13 20:41:11 UTC 2021


On Wednesday, 13 October 2021 at 19:18:55 UTC, Andrei 
Alexandrescu wrote:
> The point is that with keeps the entire expression live, even 
> if you only care about a part of it:
>
> struct S { int x; ... }
>
> with (auto y = S().x)
> {
>    ...
> }
>
> In this case the S() temporary will be destroyed only at the 
> end of with's scope.

Not a fan. It uses the same syntax as `if (auto y = S().x)` but 
has subtly different semantics, in a way that's likely to go 
unnoticed in common usage.

It's also not obvious which sub-expressions get kept alive. For 
example:

     // What's kept alive--S(1), S(2), or both?
     bool b = /* ... */;
     with (auto y = (b ? S(1) : S(2)).x)

     // Are S(1) and S(2) kept alive?
     S fun(S a, S b) { /* ... */ }
     with (auto y = fun(S(1), S(2)).x)

Of course one could work out a set of rules for this and add them 
to the language spec, but it seems like a lot of effort and 
complexity for not much benefit, especially when we can already 
get the desired behavior with normal block scopes:

     {
         S temporary = S();
         with(auto y = temporary.x)
         {
             /* ... */
         }
     } // temporary's lifetime ends here

> This enables idioms such as "I want to create a temporary 
> string and mess with it".

You can already do this by wrapping the string in a struct that 
owns its memory:

     struct OwnedString
     {
         string[] payload;
         ~this() { free(payload.ptr); }
         // etc.
     }

That way, D's existing scoping rules will ensure that the 
resources are released at the correct time, and you do not have 
to worry about keeping some unrelated temporary alive via obscure 
special-case language rules.


More information about the Digitalmars-d mailing list