www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Inlined out arguments declaration

reply Anton Pastukhov <mail anton9.com> writes:
Currently, there is no special treatment for `out` function args:

```d
bool tryParseURL(string value, out URL url) {
     // snip
}

// elsewhere

URL url;
URL parsed;

if (tryParseURL("http://example.com"), parsed)) {
     url = parsed;
}

assert(url == "http://example.com");
```



```d
URL url;

if (tryParseURL("http://example.com"), out URL parsed)) {
     url = parsed;
}

assert(url == "http://example.com");
assert(parsed == "http://example.com"); // parsed is _not_ scoped 
to if
```

Not a big deal, but it's IMO better ergonomic
Jul 19
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 20/07/2026 12:02 AM, Anton Pastukhov wrote:
 Currently, there is no special treatment for `out` function args:
 
 ```d
 bool tryParseURL(string value, out URL url) {
      // snip
 }
 
 // elsewhere
 
 URL url;
 URL parsed;
 
 if (tryParseURL("http://example.com"), parsed)) {
      url = parsed;
 }
 
 assert(url == "http://example.com");
 ```
 

 
 ```d
 URL url;
 
 if (tryParseURL("http://example.com"), out URL parsed)) {
      url = parsed;
 }
 
 assert(url == "http://example.com");
 assert(parsed == "http://example.com"); // parsed is _not_ scoped to if
 ```
 
 Not a big deal, but it's IMO better ergonomic
I considered this as part of opUnwrapIfTrue, and it has properties that make me as a compiler developer rather unhappy with. Consider: ```d struct Type1 { ~this() {} } struct Type2 { ~this() {} } if (foo(Type1 har).bar(Type2 dar)) { } else { } ``` Where do you put the destructor calls? If its in the true branch, `har` can have a valid value, but not `dar`, and then won't get its destructor called. Also what happens if `bar` throws an exception? If you put it outside the if, now you have to scope the variables outside of the if statement, and make them accessible to the else branch. It also means you can have double initialization, once in the function, and once outside. To implement this, it would require a new tree walk to extract the variable declarations out. Not the cheapest thing. There are other variants of this pattern, like logical or and and expressions. So there is no special casing our way out of this.
Jul 19
parent reply Anton Pastukhov <mail anton9.com> writes:
On Sunday, 19 July 2026 at 15:15:59 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 On 20/07/2026 12:02 AM, Anton Pastukhov wrote:
 [...]
I considered this as part of opUnwrapIfTrue, and it has properties that make me as a compiler developer rather unhappy with. [...]
This is a much broader approach than my proposal. I'm talking about a very focused case: declaring `out` variables inline, not about hoisting arbitrary code.
Jul 20
next sibling parent reply Anton Pastukhov <mail anton9.com> writes:
On Monday, 20 July 2026 at 16:52:42 UTC, Anton Pastukhov wrote:
 On Sunday, 19 July 2026 at 15:15:59 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 On 20/07/2026 12:02 AM, Anton Pastukhov wrote:
 [...]
I considered this as part of opUnwrapIfTrue, and it has properties that make me as a compiler developer rather unhappy with. [...]
This is a much broader approach than my proposal. I'm talking about a very focused case: declaring `out` variables inline, not about hoisting arbitrary code.
Reading opUnwrapOfTrue thread now. There's a Walter's response, let me quote it verbatim: On Monday, 20 July 2026 at 16:52:42 UTC, Anton Pastukhov wrote:
 On Sunday, 19 July 2026 at 15:15:59 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 On 20/07/2026 12:02 AM, Anton Pastukhov wrote:
 [...]
I considered this as part of opUnwrapIfTrue, and it has properties that make me as a compiler developer rather unhappy with. [...]
This is a much broader approach than my proposal. I'm talking about a very focused case: declaring `out` variables inline, not about hoisting arbitrary code.
Reading opUnwrapOfTrue thread now. There's a Walter's response, let me quote it verbatim:
This is a bit simpler and doesn't require language changes:
```d
struct Result(T)
{
    bool hasValue;
    T value;

    bool get(out T x)
    {
        if (hasValue)
        {
            x = value;
            return true;
        }
        return false;
    }
}

void bar(int);

void foo()
{
    Result!int r;
    int x;
    if (r.get(x)) { bar(x); }
}
```
The only difference in my proposal is that the last part would look like this: ```d void foo() { Result!int r; if (r.get(out int x)) { bar(x); } } ```
Jul 20
parent Anton Pastukhov <mail anton9.com> writes:
On Monday, 20 July 2026 at 17:03:29 UTC, Anton Pastukhov wrote:

 let me quote it verbatim:
 let me quote it verbatim:
Ugh, D's forum engine strikes again
Jul 20
prev sibling parent reply Juraj <junk vec4.xyz> writes:
On Monday, 20 July 2026 at 16:52:42 UTC, Anton Pastukhov wrote:
 On Sunday, 19 July 2026 at 15:15:59 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 On 20/07/2026 12:02 AM, Anton Pastukhov wrote:
 [...]
I considered this as part of opUnwrapIfTrue, and it has properties that make me as a compiler developer rather unhappy with. [...]
This is a much broader approach than my proposal. I'm talking about a very focused case: declaring `out` variables inline, not about hoisting arbitrary code.
I had a go on this. Made a POC that supports ```d if (Boo(out int x, out float y)) { ... } ``` But sadly, destructors are the road block. One place DMD does something like this (<https://dlang.org/spec/statement.html#condition-variables>) it spacial case destructor handling. The that would more challenging for arbitrary call expressions. If one would made types with `~this` an error for *Inlined var declarations*, than the implementation is quite straightforward (rewrite the calls `CommaExp`) - Destructors on classes in not guaranteed. So the dodged this problem. Juraj
Jul 21
parent reply Anton Pastukhov <mail anton9.com> writes:
On Tuesday, 21 July 2026 at 16:55:04 UTC, Juraj wrote:
 On Monday, 20 July 2026 at 16:52:42 UTC, Anton Pastukhov wrote:
 On Sunday, 19 July 2026 at 15:15:59 UTC, Richard (Rikki) 
 Andrew Cattermole wrote:
 On 20/07/2026 12:02 AM, Anton Pastukhov wrote:
 [...]
I considered this as part of opUnwrapIfTrue, and it has properties that make me as a compiler developer rather unhappy with. [...]
This is a much broader approach than my proposal. I'm talking about a very focused case: declaring `out` variables inline, not about hoisting arbitrary code.
I had a go on this. Made a POC that supports ```d if (Boo(out int x, out float y)) { ... } ``` But sadly, destructors are the road block. One place DMD does something like this (<https://dlang.org/spec/statement.html#condition-variables>) it spacial case destructor handling. The that would more challenging for arbitrary call expressions. If one would made types with `~this` an error for *Inlined var declarations*, than the implementation is quite straightforward (rewrite the calls `CommaExp`) - Destructors on classes in not guaranteed. So the dodged this problem. Juraj
Thanks, and I'm glad I'm not the only one bothered with the existing implementation. The problem with destructors both you and Rikki Cattermole mentioned honestly eludes me. ```d void foo() { if (Boo(out int x, out float y)) { ... } } ``` should be functionally identical to ```d void foo() { int x; int y; if (Boo(x, y)) { ... } } ``` ...which is perfectly fine D code. There's no behavior change, including the behavior of destructors.
Jul 21
parent reply Juraj <junk vec4.xyz> writes:
On Tuesday, 21 July 2026 at 17:44:48 UTC, Anton Pastukhov wrote:

 Thanks, and I'm glad I'm not the only one bothered with the 
 existing implementation.
 The problem with destructors both you and Rikki Cattermole 
 mentioned honestly eludes me.

 ```d
 void foo() {
     if (Boo(out int x, out float y)) {
         ...
     }
 }
 ```
 should be functionally identical to
 ```d
 void foo() {
     int x;
     int y;

     if (Boo(x, y)) {
         ...
     }
 }
 ```
 ...which is perfectly fine D code. There's no behavior change, 
 including the behavior of destructors.
Basically having the `x` and `y` be in the scope of `foo` and not only the `if(true)` branch diminish a lot of the value, at that point, just write it out. Juraj
Jul 21
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 22/07/2026 5:56 AM, Juraj wrote:
 On Tuesday, 21 July 2026 at 17:44:48 UTC, Anton Pastukhov wrote:
 
 Thanks, and I'm glad I'm not the only one bothered with the existing 
 implementation.
 The problem with destructors both you and Rikki Cattermole mentioned 
 honestly eludes me.

 ```d
 void foo() {
     if (Boo(out int x, out float y)) {
         ...
     }
 }
 ```
 should be functionally identical to
 ```d
 void foo() {
     int x;
     int y;

     if (Boo(x, y)) {
         ...
     }
 }
 ```
 ...which is perfectly fine D code. There's no behavior change, 
 including the behavior of destructors.
Basically having the `x` and `y` be in the scope of `foo` and not only the `if(true)` branch diminish a lot of the value, at that point, just write it out. Juraj
I would go further, it completely removes the benefit. The problem is having a get without a check that you have a value. Its its own class of bug, one that I want to entirely eliminate out of existence. Consider: ```d if (foo(int* ptr1) || bar(int* ptr2)) { int val1 = *ptr1; int val2 = *ptr2; } ``` You can't prove that this will not dereference null, they are mutually exclusive. And before you say, oh we can disallow logical or expressions! Nope. ```d bool or(bool a, bool b) => a || b; if (foo(int* ptr1).or(bar(int* ptr2))) ``` Boom, same problem. You have to decouple the check from the get entirely. So separate calls.
Jul 21
parent reply Juraj <junk vec4.xyz> writes:
On Tuesday, 21 July 2026 at 21:20:53 UTC, Richard (Rikki) Andrew 
Cattermole wrote:

 Consider:

 ```d
 if (foo(int* ptr1) || bar(int* ptr2)) {
 	int val1 = *ptr1;
 	int val2 = *ptr2;
 }
 ```

 You can't prove that this will not dereference null, they are 
 mutually exclusive.

 And before you say, oh we can disallow logical or expressions! 
 Nope.
This is legal D code, not sure it is related to the proposed *inline declaration*. ```d int* ptr1; int* ptr2; if (foo(int* ptr1) || bar(int* ptr2)) { int val1 = *ptr1; int val2 = *ptr2; } bool foo(out int* p) { p = null; return true; } bool boo(out int* p) { p = null; return true; } ```

 TryGet.
And I use this pattern in D daily, the only issue I have is the scope of the out vars.
Jul 22
next sibling parent Juraj <junk vec4.xyz> writes:
FIXED:

```d
int* ptr1;
int* ptr2;
if (foo(ptr1) || bar(ptr2)) {
     int val1 = *ptr1;
     int val2 = *ptr2;
}

bool foo(out int* p) {
     p = null;
     return true;
}

bool bar(out int* p) {
   p = null;
   return true;
}
```
Jul 22
prev sibling next sibling parent user1234 <user1234 12.de> writes:
On Wednesday, 22 July 2026 at 08:25:49 UTC, Juraj wrote:
 On Tuesday, 21 July 2026 at 21:20:53 UTC, Richard (Rikki) 
 Andrew Cattermole wrote:

 Consider:

 ```d
 if (foo(int* ptr1) || bar(int* ptr2)) {
 	int val1 = *ptr1;
 	int val2 = *ptr2;
 }
 ```

 You can't prove that this will not dereference null, they are 
 mutually exclusive.

 And before you say, oh we can disallow logical or expressions! 
 Nope.
This is legal D code, not sure it is related to the proposed *inline declaration*. ```d int* ptr1; int* ptr2; if (foo(int* ptr1) || bar(int* ptr2)) { int val1 = *ptr1; int val2 = *ptr2; } bool foo(out int* p) { p = null; return true; } bool boo(out int* p) { p = null; return true; } ```

 TryGet.
And I use this pattern in D daily, the only issue I have is the scope of the out vars.
"out vars" is a limited point of view. Imagine a variable declaration as an expression. [Everything is more simple](https://styx-lang.gitlab.io/styx/primary_expressions.html#vardeclexpression). You see, this is something I really worked on; I still believe this would be nice for D. Problem is the way we think default init. Styx is "zeroes", D is "poison you can track". Now about the scope problem. There's no problem. You can test the idea with styx.
Jul 22
prev sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 22/07/2026 8:25 PM, Juraj wrote:
 This is legal D code, not sure it is related to the proposed /inline 
 declaration/.
You have misunderstood the purpose of this syntax, and its implications. The TryGet pattern is unlikely to result in a null value if it returns true. analysis capabilities for nullability specifically to solve this. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/nullable-analysis#conditional-postconditions-notnullwhen-maybenullwhen-and-notnullifnotnull bool TryGetMessage(string key, [NotNullWhen(true)] out string? message) { if (_messageMap.ContainsKey(key)) message = _messageMap[key]; else message = null; return message is not null; } ``` Being legal, and being correct code is two vastly different things. This particular feature being proposed has limited uses outside of the TryGet pattern, so its correctness and program security must be considered and weighted quite heavily compared to other use cases.
Jul 22
next sibling parent reply Anton Pastukhov <mail anton9.com> writes:
On Wednesday, 22 July 2026 at 09:28:59 UTC, Richard (Rikki) 
Andrew Cattermole wrote:
 This particular feature being proposed has limited uses outside 
 of the TryGet pattern.
The limited scope of the proposal is intentional. I believe that being a small, low-risk change is the only possible way for it to see the light of day. user1234's idea, as interesting as it is, is unlikely to happen in D. This is really just a minor syntax sugar/ergonomic improvement. In 2026 D feels old-school and boxy sometimes.
Jul 22
parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 22/07/2026 10:10 PM, Anton Pastukhov wrote:
 On Wednesday, 22 July 2026 at 09:28:59 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 This particular feature being proposed has limited uses outside of the 
 TryGet pattern.
The limited scope of the proposal is intentional. I believe that being a small, low-risk change is the only possible way for it to see the light of day. user1234's idea, as interesting as it is, is unlikely to happen in D.
The limited scope of this proposal isn't an issue. Its not far off from what I'd do, if I was to pursue it. See my comment a bit later on in that message: "This particular feature being proposed has limited uses outside of the TryGet pattern, so its correctness and program security must be considered and weighted quite heavily compared to other use cases."
 This is really just a minor syntax sugar/ergonomic improvement. In 2026 
 D feels old-school and boxy sometimes.
When Nic introduced variable declaration support to with statements, it broke code. This could be a lot worse depending upon implementation and exact grammar changes. The potential here is for it to infect non-function-argument expressions.
Jul 22
prev sibling parent reply Juraj <junk vec4.xyz> writes:
On Wednesday, 22 July 2026 at 09:28:59 UTC, Richard (Rikki) 
Andrew Cattermole wrote:

 You have misunderstood the purpose of this syntax, and its 
 implications.
I am saying, you are dismissing this idea on a premise, that something already allowed in the language is problematic. So I do not see how that as good argument.
Jul 22
next sibling parent Nick Treleaven <nick geany.org> writes:
On Wednesday, 22 July 2026 at 10:19:26 UTC, Juraj wrote:
 I am saying, you are dismissing this idea on a premise, that 
 something already allowed in the language is problematic.
 So I do not see how that as good argument.
That is backwards - adding special language support for something that is problematic is by definition problematic.
Jul 22
prev sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 22/07/2026 10:19 PM, Juraj wrote:
 On Wednesday, 22 July 2026 at 09:28:59 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 
 You have misunderstood the purpose of this syntax, and its implications.
I am saying, you are dismissing this idea on a premise, that something already allowed in the language is problematic. So I do not see how that as good argument.
I have not dismissed this based upon what is already existing in the language. I have dismissed it due to: 1. Interaction with other language features. mainstream language & ecosystem example of this. what classes of bugs we would be recommending people to adopt (which is something both me and Adam want to avoid, if not outright disallow). 4. Introduces new failure cases that require static analysis which I would be the one having to implement AND that static analysis requires attributes to model that people have to use AND there is a much simpler alternative that entirely eliminates that class of bug AND this other feature has been used in multiple mainstream languages & ecosystems without any problems AND this other features doesn't need any kind of static analysis. Workability of a new language feature is only the starting point, there are other concerns like fallibility, program security and if its modellable that must be considered especially if that feature is a quality of life one. I've had years to study this particular set of problems, hence my strong opinions. I do understand why this feature looks attractive.
Jul 22
parent Juraj <junk vec4.xyz> writes:
On Wednesday, 22 July 2026 at 11:19:07 UTC, Richard (Rikki) 
Andrew Cattermole wrote:


 influence and what classes of bugs we would be recommending 
 people to adopt (which is something both me and Adam want to 
 avoid, if not outright disallow).
Fair and good luck
 4. Introduces new failure cases that require static analysis 
 which I would be the one having to implement AND that static 
 analysis requires attributes to model that people have to use 
 AND there is a much simpler alternative that entirely 
 eliminates that class of bug AND this other feature has been 
 used in multiple mainstream languages & ecosystems without any 
 problems AND this other features doesn't need any kind of 
 static analysis.
This is the thing that eludes me, what new failure cases? I can do all the problematic parts right now in D, they do not emerge from this proposal. Just to clarify my position. I would love to be able to limit the "scope" of vars in `if` statements, but I acknowledge all the shortcomings. And based of my POC implementation I myself would no go further with this. all ;-)
Jul 22