0

I keep running into the situation where I need to convert a Result<A,E> into Result<B,E> with A implementing Into<B>.

The only way to achieve that that I found so far is via map(into).

Is there no Result::map_into() or similar? If no, should there be?

/// Library code //////////////////////////////////
struct A(i32);

#[derive(Debug)]
struct B(i32);

impl From<A> for B {
    fn from(a: A) -> Self {
        Self(a.0)
    }
}

type MyError = Box<dyn std::error::Error>;

fn compute_thing() -> Result<A, MyError> {
    Ok(A(42))
}

/// My code //////////////////////////////////////
fn do_computation() -> Result<B, MyError> {
    // There must be a better way to do this than with `map(into)`
    compute_thing().map(A::into)
}

fn main() {
    println!("{:?}", do_computation());
}

Proof of concept that this could be done very easily:

/// Library code /////////////////////////////////////////////////////////////
struct A(i32);

#[derive(Debug)]
struct B(i32);

impl From<A> for B {
    fn from(a: A) -> Self {
        Self(a.0)
    }
}

type MyError = Box<dyn std::error::Error>;

fn compute_thing() -> Result<A, MyError> {
    Ok(A(42))
}

/// Implementatoin of map_into ///////////////////////////////////////////////
trait MapInto<T> {
    fn map_into(self) -> T;
}

impl<In, Out, E> MapInto<Result<Out, E>> for Result<In, E>
where
    Out: std::convert::From<In>,
{
    fn map_into(self) -> Result<Out, E> {
        self.map(|s| s.into())
    }
}

/// My code //////////////////////////////////////////////////////////////////
fn do_computation() -> Result<B, MyError> {
    // There must be a better way to do this than with `map(into)`
    compute_thing().map_into()
}

fn main() {
    println!("{:?}", do_computation());
}

Functions that I personally think would be helpful:

  • Result::map_into()
  • Result::map_err_into()
  • Option::map_into()

Or even better:

  • impl From<Option<T>> for Option<U> where U: From<T>
  • impl From<Result<T0, E>> for Result<T1,E> where T1: From<T0>
  • impl From<Result<T, E0>> for Result<T,E1> where E1: From<E0>

Or similar.

Finomnis
  • 18,094
  • 1
  • 20
  • 27
  • The `From` impls you suggest at the end of the post look reasonable to me, but I guess they run afoul of the coherence rules. There already is `From for Option`, which I think would cause problems. Moreover, I don't really see what's wrong with `.map(Into::into)` – looks perfectly fine and readable to me. – Sven Marnach May 20 '22 at 11:26
  • Yah, didn't realize you could do `.map(Into::into)`. Always specified `.map(A::into)` or `.map(B::from)` which felt like I had to specify the type redundantly. I just discovered that you could use trait functions directly, so I flagged this question as duplicate – Finomnis May 20 '22 at 11:31
  • 1
    @SvenMarnach I don't think it would cause any issue because they don't overlap: in the `From for Option`, you add an `Option` layer, whereas in `From – jthulhu May 20 '22 at 11:35
  • @BlackBeans You are right – the conflict isn't with `From for Option`, but rather with `From for T`, which rules out all three of the suggested `From` impls. – Sven Marnach May 20 '22 at 13:11
  • @SvenMarnach Is that why those are not in the standard library ... makes sense – Finomnis May 21 '22 at 08:41

0 Answers0