Skip to content

Add opUnwrapIfTrue operator overload for structs#22570

Draft
rikkimax wants to merge 1 commit intodlang:masterfrom
rikkimax:opUnwrapIfTrue
Draft

Add opUnwrapIfTrue operator overload for structs#22570
rikkimax wants to merge 1 commit intodlang:masterfrom
rikkimax:opUnwrapIfTrue

Conversation

@rikkimax
Copy link
Copy Markdown
Contributor

This is an improvement on @mustuse to allow only getting access to a value if it has been checked to be true.

Does not use DFA to do this, instead if statement + a var declaration in its condition.

import core.attribute : mustuse;

@mustuse
struct Result(Type) {
    private {
        Type value;
        bool haveValue;
    }

    this(Type value) {
        this.value = value;
        this.haveValue = true;
    }

    bool opCast(T:bool)() => haveValue;

    Type opUnwrapIfTrue() {
        assert(haveValue);
        return value;
    }
}

Result!int result = Result!int(99);

if (int value = result) {
    // got a value!
    assert(value == 99);
} else {
    // oh noes an error or default init state
}

No DIP nor has it been approved at a meeting.
It is the result of my report on an alternative exception mechanism.
I was able to take an existing approach that I've been mulling over that relied on UDA's and a DFA for path sensitivity and turn it into a simple operator overload hook instead.

@dlang-bot
Copy link
Copy Markdown
Contributor

Thanks for your pull request and interest in making D better, @rikkimax! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + dmd#22570"

@rikkimax
Copy link
Copy Markdown
Contributor Author

Closes: #20300

I will note that this issue has appeared in PhobosV2, with Nullable and its get method being available via alias this.

@rikkimax rikkimax force-pushed the opUnwrapIfTrue branch 3 times, most recently from 71514ed to 4edcf62 Compare February 14, 2026 12:23
@thewilsonator thewilsonator added the Review:Needs Spec PR A PR updating the language specification needs to be submitted to dlang.org label Feb 14, 2026
@Herringway
Copy link
Copy Markdown
Contributor

I still don't see what this has to do with @mustuse...

@jmdavis
Copy link
Copy Markdown
Member

jmdavis commented Feb 15, 2026

I still don't see what this has to do with @mustuse...

Well, I assume that it's because the thought was that this would be for a type that's returned from a function to give a result or indicate a failure (e.g. a type which holds a value or an error code / message, or something like a Nullable where there's a value upon success but nothing on failure).

That being said, I don't see anything about the proposed feature which requires @mustuse. It seems orthogonal to me. For instance, I don't think that it would make sense for Nullable to have @mustuse, since it's not just used for returning results from functions, but I could see someone wanting to use this feature on a type like Nullable. So, I don't see any reason to tie this to @mustuse.

At its core, this seems to just be proposing that if a type is explicitly convertible to bool (since that's how if(cond) is implemented when cond is not a bool), and it converts to true, then you can implicitly convert it to some other type within the condition of an if statement or loop. That could be used in conjunction with @mustuse, but IMHO, it shouldn't be required.

For a type that is explicitly intended to be returned as the result of a function (as opposed to something like Nullable which is sometimes used that way but which isn't necessarily used that way), this feature could make sense, but it also runs the risk of being confusing on the basis that traditionally, stuff involving error codes (which result types like this are arguably an evolution of) is true on failure, not on success. And if you have a type which contains either a result or an error code / message, it's not necessarily obvious which should be considered true. Now, the feature could work either way (since it's the programmer specifying the result of the operator, not the compiler), and ultimately, that would be a design choice on the part of whoever wrote the result type, but for the sake of clarity, it would arguably be better design to not have the result type implicit convert to either the good result or the error code / message and instead have explicit function calls. That way, it would be self-documenting. So, I don't think that this feature is a good fit for that use case.

For a Nullable type which isn't necessarily being used as a function result, having it give the result if explicitly converting the object to bool results in true would arguably make more sense than for a proper result type, because then the question is whether it has a value and not whether it has a value or it has an error code / message. Of course, in that case, another way that this could be modeled is to treat the Nullable type like a pointer. So, using the Nullable object in an if condition would result in true if it has a value, and then within the if statement, you just dereference the object rather than extracting it from the Nullable within the if condition as is proposed here.

So, my gut reaction is that this proposed feature really doesn't make sense for a proper result type, because a proper result type shouldn't just be a value or not. It should have a value or information on the error that occurred. And for a Nullable / Option type, it can already just use the pointer syntax. And if you do that, then there's no need for a new feature.

I can see why someone might want this, but I'm not sure that it ultimately makes sense. I'd argue that overloading opCast!bool and opUnary!"*" would be a better solution.

@rikkimax
Copy link
Copy Markdown
Contributor Author

I can see why someone might want this, but I'm not sure that it ultimately makes sense. I'd argue that overloading opCast!bool and opUnary!"*" would be a better solution.

This requires opCast!bool to determine branch.
It is only for structs, and there is no requirement for @mustuse to be in use, its how I intend for it to be used however.

The problem with splitting out the getting of value is you can do it without the check.
You can't get without the check with this.

This guarantees that it is paired together.
Eliminating a rather furstrating almost guaranteed to happen problem where I wasn't checking correctly repeatedly over many year period and got a runtime error. Not at all fun.

@rikkimax rikkimax force-pushed the opUnwrapIfTrue branch 4 times, most recently from 2be2f18 to 9ae65ec Compare February 17, 2026 11:21
@rikkimax
Copy link
Copy Markdown
Contributor Author

I've overhauled the implementation, making it simpler.
But most importantly, it's more correct now.

I've included in changelog explicit mentions that mustuse is not required.

@rikkimax rikkimax force-pushed the opUnwrapIfTrue branch 2 times, most recently from 086ea80 to 29d400e Compare March 14, 2026 23:52
@rikkimax
Copy link
Copy Markdown
Contributor Author

I am applying feedback to PR, specifically making it much more explicit that an unwrap is occuring with a function call.

To trigger it, it's via an alias, to the operator overload, it is not required.
For the test I called the alias unwrap, but it'll TBD on what the idiom would have it be.

Comment on lines +43 to +48
if (int i = oi.unwrap) {
// got a value!
assert(i == 2);
} else {
assert(0); // error, there is meant to be a value
}
Copy link
Copy Markdown
Member

@ibuclaw ibuclaw Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this be done with monadic operators, to take a leaf from C++ if we're going with Optional as the reference example.

oi.and_then((i) { assert(i == 2); return Optional!int(i); })
   .or_else(() {  assert(0); });

Or something like that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You lose control flow, no more returning which is a common thing to want to do in the else, and even the true.

It also introduces a lot of REALLY FUN THINGS, like closure context's which right now don't do cleanup.

Copy link
Copy Markdown
Member

@ibuclaw ibuclaw Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea what you are referring to. No heap closure is involved.

struct Optional(Type) {
...
    // Omitting T function(...) overloads for brevity
    auto and_then(T)(scope T delegate(Type) fun)
        => (haveValue) ? Optional!T(fun(value)) : Optional!T();

    auto or_else(scope Type delegate() fun)
        => (haveValue) ? this : Optional!Type(fun());

    auto transform(T)(scope T delegate(Type) fun)
        => (haveValue) ? Optional!T(fun(value)) : Optional!T();

    Type value_or(Type fallback)
        => (haveValue) ? value : fallback;
}
string test(Optional!string v)
{
    import std.conv;
    return v
        .or_else(() => "0")
        .and_then((s) => to!int(s))
        .transform((n) => n + 1)
        .transform((n) => to!string(n))
        .value_or("NaN");
}

Optional!string("43").test.writeln;  // "44"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Review:Needs Spec PR A PR updating the language specification needs to be submitted to dlang.org

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants