Open sfackler opened 5 years ago
So, if nobody objects, I will open a PR with the "Remove chain() and add sources() instead" solution, as nothing else provides an ergonomic solution.
Here we go: https://github.com/rust-lang/rust/pull/81705
I think @haraldh touched on a reason to consider both a method that iterates over self
and its sources and one that just includes its sources: you can call the latter on any error, but can only call the former on an error you can cast into dyn Error + 'static
:
// Valid for any `&E: Error + ?Sized`
fn sources(&self) -> Chain<'_> {}
// Valid only for `&E: Error + Sized + 'static`
fn chain(&self) -> Chain<'_> where Self: Sized + 'static {}
So you could write a maximally compatible method to display an error using sources
:
fn render(err: impl Error) {
fn render_inner(err: impl Error) { .. }
render_inner(&err);
for source in err.sources() {
render_inner(source);
}
}
but you couldn't write that same implementation using chain
unless you also constrain the input error to 'static
.
You could argue that's a bit of a forced difference though, because in practice I think non-'static
errors aren't very useful anyway since they can't participate in the source chain, but currently unless you wrap a Box<dyn Error + 'static>
up into a newtype that implements Error
the only impl Error
you can get from it is non-'static
.
Shouldn't the iterator proper implement FusedIterator
?
Shouldn't the iterator proper implement
FusedIterator
?
That's not obvious. There is no documented requirement on https://doc.rust-lang.org/1.53.0/std/error/trait.Error.html#method.source that it behave in a fused way.
Shouldn't the iterator proper implement
FusedIterator
?That's not obvious. There is no documented requirement on https://doc.rust-lang.org/1.53.0/std/error/trait.Error.html#method.source that it behave in a fused way.
Apologies if I'm misinterpreting something, but once the Error
object returns a None
, that's the end of the error chain, isn't it? You can't get another &(dyn Error + 'static)
out of that. I guess you could create an infinite loop, but ending is not required for fused iterators.
Nothing says the error can't change its mind about what its source is:
impl Error for SillyError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
if rand::int() == 1 { Some(&self.source) } else { None }
}
}
Nothing says the error can't change its mind about what its source is:
impl Error for SillyError { fn source(&self) -> Option<&(dyn Error + 'static)> { if rand::int() == 1 { Some(&self.source) } else { None } } }
Is it a requirement for fused iterators to not involve randomness? All I'm saying is that std::error::Chain
could implement FusedIterator
, since, unless there are plans to change the way that it is implemented, it could get a marginal win in the albeit rare case where one passes it into a function that fuses it.
Is it a requirement for fused iterators to not involve randomness?
I guess my example might still not be enough as std::error::Chain
will assume it's over. The rule for FusedIterator
is that once None
is returned, None
will always be returned on subsequent .next()
calls. I suppose whether the last Error
is delegated to or if there is some implicit FusedIterator
assumption in std::error::Chain
would answer this question.
In #100955, I renamed chain
to sources
, it still includes self in the iterator. I added a comment to try and record the issue with why source
must be defined in the impl and not the trait. In terms of solutions, this should be fixed by the dyn*
work which will permit more methods on trait objects. The other possible solution is to remove self
from the iterator (this is a solution because the problem is in converting self
to a trait object, which might not work if self
is itself a wide pointer which is not a trait object, all the other sources are already trait objects so skipping self
means no conversion has to happen). Adding an Unsize bound is not a solution because its not backwards compatible.
I recently discovered that io::Error
doesn't return custom inner errors through Error::source
(https://github.com/rust-lang/rust/issues/101817). I wonder whether we should work around that for iter_chain
and iter_sources
so that they know how to walk through (and continue after) io::Error::Custom
as well?
Solutions to the un-ergonomic usage
Another solution is to make sources
a function in the module core::error
, rather than a method on dyn Error
. One would write use std::error;
and then error::sources(&error)
.
This covers the
<dyn Error>::iter_chain
and<dyn Error>::iter_sources
methods added in #58289.