Hi All, This is in connection with Vicente's question: what exception safety should we expect of copy assignment of `expected`? Now, this is really funny because we are talking about exception safety in something that is used as a substitute for exceptions. That is, one of the use cases for `expected` is to be able to get rid of exception handling altogether (while still reporting failures). But this fundamental problem, "what state my object is in if this mutating operation fails" is not specific to exceptions. It is speciffic to operations that might fail; and how (and if) they signal failures is of secondary importance. So, let's talk about *failure safety* rather than 'exception safety'. First, let's consider the use case for `expected`, where we want to disable exception handling altogether. This means `T` or `E` cannot throw on any operation. But copy assignment can still fail, right? Or maybe in these domains you are only using trivially-copyable types as `T` and `E`. Or maybe in these domains you never have a need to copy-assign instances of `expected`? Is so, they provide a *no-fail* guarantee, and any implementation of `expected` will be good and offer no-fail guarantee also. Bus if some copying operation on T or E can fail, how is the failure reported? Through a return value? Output parameter? But whatever the answer, we are arriving at the "nested failure" problem: we are processing a (potential) failure report, and this processing fails. What should we do? report the new error condition and ignore the previous? This is very close to a double-exception during stack unwinding. In C++ it std::terminates, other languages ignore the original error, or build a combined error report. All these solutions not satisfactory, and maybe no satisfactory solution exists. I would like to hear an opinion from people who deal with `expected` in exception-disabled environments. On the other extreme, you fave my example with parsing input (which Vicente observed is not parsing, but matching): https://github.com/akrzemi1/__sandbox__/blob/master/outcome_practical_exampl... In that case, If I get an exception anywhere (not only upon copying T or E) I want stack to be unwound so far, that all not `expected` objects will remain. So I only care about basic failure guarantee: just let me correctly destroy these objects. In the middle: you have the situation where you copy-assign an `expected` and a copy-constructor of assignment of T or E throws. But where did this exception come from, given that you are using `excepted` for signalling failures? Or are you signalling some failures with exceptions and some with `expected`? And if so, are exceptions not more panic-like? And in that case yu would like to abandon the processing of any `expected`? Anyway, the most difficulties stem from the case where you are storing an E in `expected` and you want to assign an `expected` storing T. You have to first destroy E, and then may not be able to construct a T. My solution to this would be to go to the advice from the first days of forming exception safety guarantees: provide basic guarantee by default, and strong guarantee only if it does not cost too much. We are used to STL containers providing strong assignment, but this is because they are pointers, and they can implement it for free. But does std::touple provide a strong guarantee? No. Do aggregate types provide stron guarantee? No. And can it result in inconsistent data? It can: ``` struct Man { std::string fist_name, last_name; }; Man m1 = {"April", "Jones"}; Man m2 = {"Theresa", "May"}; try { m2 = m1; } catch(...) { } ``` `m2` may end up being {"April", "May"}. And we are taught to write types like this. But if this hapens, the blame is on whoever allowed these objects to outlive the "stack unwinding bubble". So my view, as of today, is not to strive for a strong or even never-empty guarantee. Provide a conditional guarantee, if types T and E don't throw, you get no-fail guarantee. If they do, you only have a basic guarantee: you can destroy, assign to, or maybe call valueless_by_exception(). Nothing more. I think `std::variant` made the optimal choice. And people should code so that instances of `expected` (at least those with T or E throwing on copy/move) should not outlive the "stack unwinding bubbles". Also, note what vector::push_back does when T is non-opyable and its move constructor is potentially throwing: if T's move throws upon reallocation, the behavior is undefined. Regards, &rzej;