DIP 1038: @nodiscard - Unofficial "post-final" review

Paul Backus snarwin at gmail.com
Mon Feb 22 23:32:23 UTC 2021

On Monday, 22 February 2021 at 22:01:01 UTC, Andrei Alexandrescu 
> This is nice and well argued. The time-tested presence of the 
> attribute in C++ is important, probably could use 1-2 
> paragraphs to emphasize that. (It's been a non-standard 
> extension for a long time and clearly there was a lot of demand 
> to standardize it.)

Are there any well-known [[nodiscard]] success stories that the 
DIP could cite? I have little personal experience with C++, and 
do not know where to look for examples outside of the standard 

> For integration, it would be nice if the proposal made pure 
> non-void functions automatically nodiscard. That way ignoring 
> the reusult of such a function is an error, as it should.

The compiler currently issues a warning when the return value of 
a strongly-pure, nothrow, non-void function is discarded. I agree 
that there is a case to be made that this should be an error 
instead, but I think the appropriate place to make that case is 
in an enhancement request on Bugzilla, not DIP 1038.

> The proposal should mention what happens if two declararions 
> differ only by nodiscard:
> void* malloc(size_t);
> @nodiscard void* malloc(size_t);
> I forgot what we usually do. I think it's safe to mark that as 
> as error.

@nodiscard is a user-defined attribute, and the compiler 
currently allows multiple declarations of a function that differ 
only by UDAs. [1] Again, I agree that this should probably be an 
error, but since it is an issue that affects UDAs in general, it 
should be addressed in a Bugzilla issue, not DIP 1038.

> The proposal should mention how @nodiscard works with 
> overriding. Given that @nodiscard is more restrictive, by the 
> old OOP principles a @nodiscard function should be overridable 
> by a non- at nodiscard function. (Rationale: overriding functions 
> ASK LESS and RETURN MORE.) However, a practicality 
> consideration is that the author of the override simply forgot 
> to add @nodiscard, so we can make the attribute equivariant. 
> It's important in all cases that a no- at nodiscard function 
> cannot be overriden by a @nodiscard one.

@nodiscard is a UDA, and UDAs are not inherited, so applying 
@nodiscard to a base-class method will have no affect on 
derived-class methods that override it.

The way to achieve the outcome you desire here is to apply 
@nodiscard to the method's return type, rather than the method 

     @nodiscard struct Result { int unwrap; }

     class A { Result func() { return Result(1); } }
     class B : A { override Result func() { return Result(2); } }

The idea behind the proposed design is that if you want strong 
guarantees, you should be applying @nodiscard to your types, not 
to your functions. If you are willing to accept weaker guarantees 
in return for greater ease-of-use, that's when you should use 
@nodiscard as a function attribute. (This design is cribbed from 
Rust's `#[must_use]` attribute, which works the same way.)

Of course, the fact we're having this exchange means that the 
above explanation really ought to be in the DIP itself.

Thanks for your comments!

[1] https://run.dlang.io/is/tK14gJ

More information about the Digitalmars-d mailing list