5 ms·
Comparing Exceptions and Errors in D
- giancarlostoro 4y agoI am still unclear on what mustUse is or does is there any sample code?
- Doxin 4y agoIt'll throw an error for myFunc(); but not for auto foo = myFunc(); if myFunc returns a mustUse type.
- giancarlostoro 4y agoThank you, I know it looks stupid simple but code helps me put it into a perspective I can remember better vs only reading documentation about code. That is kind of an interesting way to sort of force someone to not just throw away a return value. Question: Do you know if this would also be fine if the called function was in a return statement?
- Doxin 4y agoThis will only cause an error if both of these are true: - if the expression is the top level expression in a statement (like in my example code) or the left hand side in a comma expression - if the expression is not an assignment, increment, or decrement So using it in a return is fine because then the expression is no longer the top level in the statement, the return is. Of course then the function calling THAT function must deal with not discarding the value. It's pretty neat.
- Aisen8010 4y agoThe official documentation[0] is cryptic as well. [0] https://dlang.org/library/core/attribute/mustuse.html https://dlang.org/library/core/attribute/mustuse.html
- Simon_O_Rourke 4y agoI've always wondered if the Dlang docs are intentionally kept vague and cryptic.
- loeg 4y agoSeems pretty similar to nodiscard in C++17 and previously, GCC attribute warn_unused_result. https://en.cppreference.com/w/cpp/language/attributes/nodiscard https://en.cppreference.com/w/cpp/language/attributes/nodisc... https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attribute...
- mhh__ 4y agoCryptic in what way? I can fix it but the example given is clear, no?
- TingPing 4y agoI think the example is very clear.
- schveiguy 4y agoOof, seems I got the camelcase wrong. It's `@mustuse` Working on updating that. I think the reason the documentation is cryptic is because it's a library-supplied User Data Attribute that is specially recognized by the compiler. The documentation generator is having trouble with it. In code it's pretty simple: https://github.com/dlang/druntime/blob/705fb36e5fc4d930eed85bf0166273c923117108/src/core/attribute.d#L292 https://github.com/dlang/druntime/blob/705fb36e5fc4d930eed85...
- mhh__ 4y ago@mustUse is a user defined attribute the compiler recognizes that forces return values it affects to be used. e.g. if you return something that could contain an error then ignoring the return value could be disastrous.
- loeg 4y agoA similar concept is used widely in glibc under the name “__wur” (shorthand for __attribute__((warn_unused_result))). https://github.com/bminor/glibc/search?q=__wur&type= https://github.com/bminor/glibc/search?q=__wur&type=
- Georgelemental 4y agoRust also has something similar, in the form of the `#[must_use]` attribute.
- loeg 4y agoYeah, and one called `[[nodiscard]]` in C++17 as well.
- giancarlostoro 4y agoI kind of get this much, but it would be nice to see a small set of code showcasing this in practice to really cement it in for me.
- simcop2387 4y agoIf you want to look at it from an academic perspective it tells the compiler that it should be a relevant[1] type. The idea is that you can make the compiler force the use of the value at least once. This is great for error or result types so that you can require the caller of your code actually check for an error or valid result, this way you can't forget to do it (if you do it's a compile error and you never get to run the code). [1] https://en.wikipedia.org/wiki/Substructural_type_system#Different_substructural_type_systems https://en.wikipedia.org/wiki/Substructural_type_system#Diff...
- twic 4y ago> However, a nothrow function is still allowed to throw an Error. How does that work? > How it works is that the compiler still omits exception cleanup code, and the code that catches the Error is not allowed to continue the program. If it does, the program may obviously be in an invalid state. Yikes? What's the rationale for allowing errors to be thrown out of nothrow functions, rather than just aborting there and then?
- schveiguy 4y agoThe rationale is that in a correctly written program, an Error should never be thrown. It's similar to UB in C. The compiler can assume Errors are never thrown, and so it can not worry about cleanup in nothrow functions. But the nice thing is, it reuses the same handling mechanisms as exceptions. You don't have to create something different for handling Errors. In fact, the code that prints error stack traces and exits is the same code that handles exceptions. Consequently, this is why you shouldn't catch them, or at least not catch them and continue. The exceptions are unittests and contract asserts, where the compiler does guarantee proper stack unwinding.
- colejohnson66 4y agoIf I understand, a throw in a nothrow would be used for completely unrecoverable situations? Akin to Rust’s panic!() macro?
- twic 4y ago> The rationale is that in a correctly written program, an Error should never be thrown. [...] you shouldn't catch them, or at least not catch them and continue In that case, why not just abort? Why give the user a footgun like this?
- twic 4y agoFWIW, C++'s noexcept specifier turns exceptions into aborts rather than letting them escape [1]: > Non-throwing functions are permitted to call potentially-throwing functions. Whenever an exception is thrown and the search for a handler encounters the outermost block of a non-throwing function, the function std::terminate or std::unexpected (until C++17) is called: Microsoft's dialect of C++ has a nothrow attribute which is purely advisory, like D's, and is now deprecated in favour of noexcept [2]: > We recommend that all new code use the noexcept operator rather than __declspec(nothrow). > This attribute tells the compiler that the declared function and the functions it calls never throw an exception. However, it does not enforce the directive. In other words, it never causes std::terminate to be invoked, unlike noexcept, or in std:c++17 mode (Visual Studio 2017 version 15.5 and later), throw(). Rust has panics, which by default unwind, like exceptions, but can be compiled to abort instead [3]: > By default, when a panic occurs, the program starts unwinding, which means Rust walks back up the stack and cleans up the data from each function it encounters. However, this walking back and cleanup is a lot of work. Rust, therefore, allows you to choose the alternative of immediately aborting, which ends the program without cleaning up. I don't believe there is any stable or planned way to declare that normal Rust functions cannot panic (so should abort instead of letting the panic escape). But there is some work towards the idea that extern "C" functions should do so [4]. [1] https://en.cppreference.com/w/cpp/language/noexcept_spec https://en.cppreference.com/w/cpp/language/noexcept_spec [2] https://docs.microsoft.com/en-us/cpp/cpp/nothrow-cpp?view=msvc-170 https://docs.microsoft.com/en-us/cpp/cpp/nothrow-cpp?view=ms... [3] https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html#unwinding-the-stack-or-aborting-in-response-to-a-panic https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-... [4] https://github.com/rust-lang/project-ffi-unwind/blob/master/rfcs/0000-c-unwind-abi.md https://github.com/rust-lang/project-ffi-unwind/blob/master/...