Repository navigation
ACP: impl From<Infallible> for io::Error #86
Description
Proposal
Problem statement
Generic code sometimes needs to unify error types. This may happen in combinators or specialization-like wrappers. While Infallible should be obviously, trivially convertible to io::Error, the impl is not available.
Motivation, use-cases
My current use case (simplified)
pub struct HexWriter<W: HexWrite>(W);
/// Abstracts over fmt::Write and io::Write
pub trait HexWrite {
type Error;
fn write_byte(&mut self, byte: u8) -> Result<(), Self::Error>;
}
pub struct IoWriter<W: io::Write>(W);
impl<W: io::Write> HexWrite for IoWriter<W> { type Error = io::Error; /* ... */ }
/// Direct impl avoids dynamic dispatch of formatting and provides provably non-failing errors
impl HexWrite for String { type Error = Infallible; /* ... */ }
// Ideally, this would work for both HexWriter<String> and HexWriter<IoWriter<W>>:
impl<W> io::Write for HexWriter<W> where W: HexWrite, W::Error: Into<io::Error> { /* ... */ }Potential other use cases
I vaguely remember doing something like this in the past, I think it was related to futures or some IO:
pub enum Error<A, B> {
ErrorA(A),
ErrorB(B),
}
impl<A, B> Error<A, B> {
pub fn unify<E>(self) -> E where A: Into<E>, B: Into<E> {
match self {
ErrorA(a) => a.into(),
ErrorB(b) => b.into(),
}
}
}This allows unifying error types of different operations. If one of the operations is infallible and the other may return io::Error unify currently can not be used even though it looks like it should.
Solution sketches
Proposed solution: Impl conversion by matching on self, I don't see any downsides besides few added lines of code
Alternative solution 1: impl<T> From<Infallible> for T - this is probably far away due to overlap with impl<T> From<T> for T; the proposed solution already provides value and can be replaced with this alternative later without breaking anything
Alternative solution 2: do nothing, users can have their own IntoIoError trait (my current workaround) - this needlessly duplicates and complicates users code, adding a few lines to std seems to be a better trade-off.
Links and related work
What happens now?
This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.
Activity
Kixunil commentedon Aug 11, 2022
Actually, same applies to
fmt::Error, so I think that should be included.shepmaster commentedon Aug 11, 2022
it is not obvious to me, can you spell it out?
Kixunil commentedon Aug 11, 2022
Infalliblecan never exist in run time, so this is the only reasonable implementation:shepmaster commentedon Aug 11, 2022
If I follow your logic, then
Infallible -> ()is also a reasonable implementation. Potentially also to any type.This makes me think that I am not understanding your logic, which is why I’d appreciate if you could clarify the rationale.
cuviper commentedon Aug 11, 2022
Note that we do have
From<Infallible> for TryFromSliceErrorandTryFromIntError.There is also
From<!> for Infallible, AIUI so that they might become the same type when!is stabilized, and there's a "reserved"impl<T> From<!> for T(rust-lang/rust#64715).Kixunil commentedon Aug 11, 2022
@shepmaster actually, yes!
Infallibleshould be convertible to any type butstdis probably too much sinceInfallibleis mainly used in error types.joshtriplett commentedon May 17, 2023
We talked about this in the libs meetup. Initially there was some support for this, but we were hesitant about the effect this would have on the ecosystem in pushing people to add lots of these impls. We continue to hope that rust-lang/rust#64715 (and the stabilization of ! itself) might happen eventually (and we've poked for an update on the status of that). Meanwhile, we're going to close this..