Ho letto alcuni articoli su come Rust gestisce gli errori usando il tipo Result<T, E>
e per me sembra una soluzione ibrida di best-of-both-world (eccezioni e codici di ritorno) che può essere molto utile. Penso che sarebbe particolarmente utile per le API in cui non si sa in anticipo se determinati errori sono eccezionali e dovrebbero essere generati, o che sono semplicemente un'indicazione di qualcosa che va storto in un modo "previsto". Il ritorno di Result
consente di trasferire tale decisione agli utenti dell'API in modo non troppo intrusivo. Quindi ora sto discutendo se introdurlo o meno in un progetto C ++. La classe dei risultati potrebbe essere implementata in modo approssimativo come segue:
template< class T, class E = std::exception >
class result
{
public:
//constructors etc
operator bool() const
{
return !!res;
}
T& operator * ( )
{
if( !res )
{
assert( ex );
throw *ex;
}
return *res;
}
const E& exception() const
{
assert( ex );
return *ex;
}
private:
std::optional< T > res;
std::optional< E > ex;
};
Quindi, c'è una funzione
result< int > Read();
i chiamanti possono usare lo stile 'error-code':
const auto result = Read();
if( !result )
{
log.debug() << "failed read" << result.exception();
return false; //or again return a Result
}
doSomething( *result );
o stile "eccezione":
try
{
doSomething( *Read() );
}
catch( const std::Exception& e )
{
//do what's needed
}
o decidi che non sanno come gestire le eccezioni da Leggi e lascia che il loro chiamante a sua volta si occupi di esso
doSomething( *Read() ); //throws if !result
Questa sembra una buona idea in generale, e ci sono principi simili usati nel codice C ++ esistente? O ci sono seri svantaggi che non vedo al momento? Come è troppo complicato verso gli utenti perché ci sono troppe opzioni o forse perché non è un tipico sistema usato in C ++?