2

I'd like to remove the use of .unwrap() from code which maps over an ndarray::Array and use a Result type for get_data() instead.

extern crate ndarray;

use ndarray::prelude::*;
use std::convert::TryFrom;
use std::error::Error;

fn get_data() -> Array2<usize> {
    // In actual code, "a" comes from an external source, and the type
    // is predetermined
    let a: Array2<i32> = arr2(&[[1, 2, 3], [4, 5, 6]]);
    let b: Array2<usize> = a.map(|x| usize::try_from(*x).unwrap());
    b
}

fn main() -> Result<(), Box<dyn Error>> {
    let a = get_data();
    println!("{:?}", a);

    Ok(())
}

For Vec, I've found this trick: How do I stop iteration and return an error when Iterator::map returns a Result::Err?.

However, this does not work with Arrays (collect isn't defined, and the semantics don't quite match up, since ndarray::Array defines a block of primitive types, which (AFAIU) can't hold Results).

Is there a nice way to handle this?

Stargateur
  • 24,473
  • 8
  • 65
  • 91
kmsquire
  • 1,302
  • 12
  • 12
  • 2
    look like it's almost impossible to do, I advice you to create a issue asking for `try_map()` implementation – Stargateur Aug 31 '19 at 04:54
  • 1
    The closest thing I come up to is https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=413ae1a6e8e05fc93ee9cbba1b294704 – Stargateur Aug 31 '19 at 04:58
  • 1
    A `try_map` implementation is the way to go. Short of that, I came up with: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d13538a717bd4bee9890961f6f9d5c27 – edwardw Aug 31 '19 at 06:53
  • @Stargateur thanks for the input. I'll open an issue in `ndarray` about adding `.try_map` – kmsquire Sep 03 '19 at 20:11
  • @edwardw, thank you! For now, I'll probably go with your solution (and open an issue regarding `try_map`). Do you want to type it up as an answer so I can accept it? – kmsquire Sep 03 '19 at 20:12

1 Answers1

1

A native try_map implementation from ndarray would be ideal. It can short-circuit the computation and return as soon as an error occurs. It is also more composable.

Short of that, nothing wrong with a good old mutable sentinel variable:

extern crate ndarray;

use ndarray::prelude::*;
use std::convert::TryFrom;
use std::error::Error;
use std::num::TryFromIntError;

fn get_data() -> Result<Array2<usize>, TryFromIntError> {
    let mut err = None;
    let a: Array2<i32> = arr2(&[[1, 2, 3], [4, 5, 6]]);
    let b: Array2<usize> = a.map(|&x| {
        usize::try_from(x).unwrap_or_else(|e| {
            err = Some(e);
            Default::default()
        })
    });

    err.map_or(Ok(b), Err)
}

fn main() -> Result<(), Box<dyn Error>> {
    let a = get_data()?;
    println!("{:?}", a);

    Ok(())
}
edwardw
  • 12,652
  • 3
  • 40
  • 51