Maria Dubyaga
Maria Dubyaga

Reputation: 41

How do I catch a panic from Rayon's `par_iter()`?

I'm trying to catch the panic from inside par_iter() and continue to what I have after par_iter block.

If I have this, I get everything correctly and there's no panic:

let dog: Dog = Dog {
    name: "Dog",
    vector: vec![1, 2, 3, 4, 5],
};
let cat: Cat = Cat {
    name: "Cat",
    vector: vec![1, 2, 3],
};
let pig: Pig = Pig {
    name: "Pig",
    vector: vec![1, 2, 3, 4, 5],
};
let mut v: Vec<Box<dyn Animal>> = Vec::new();
v.push(Box::new(cat));
v.push(Box::new(dog));
v.push(Box::new(pig));

let total = v
    .par_iter()
    .map(|x| {
        println!("{} vector[1] is {:?}", &x.name(), &x.vector()[1]);
        x.vector()[1].clone()
    })
    .collect::<Vec<(i32)>>();

let sum: i32 = total.iter().sum();
println!("sum: {}", sum);

I get the sum after par_iter

Cat vector[1] is 2
Dog vector[1] is 2
Pig vector[1] is 2
sum: 6

When I try to access an index which exceeds the vector length, I still print whatever I have including the panic, but don't get to the sum:

let total = v
    .par_iter()
    .map(|x| {
        println!("{} vector[4] is {:?}", &x.name(), &x.vector()[4]);
        x.vector()[4].clone()
    })
    .collect::<Vec<(i32)>>();

let sum: i32 = total.iter().sum();
println!("sum: {}", sum);

The result:

     Running `target/debug/playground`
thread '<unnamed>' panicked at 'index out of bounds: the len is 3 but the index is 4', /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/libcore/slice/mod.rs:2717:10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
Standard Output
Dog vector[4] is 5
Pig vector[4] is 5

I tried to to check what I could do if I implemented a panic_handler:

let panic_handler = move |err: Box<dyn Any + Send>| {
    println!("hello");
};
rayon::ThreadPoolBuilder::new()
    .num_threads(2)
    .panic_handler(panic_handler)
    .build_global()
    .unwrap();

It doesn't work and it's not even used:

warning: unused variable: `err`
  --> src/main.rs:52:31
   |
52 |     let panic_handler = move |err: Box<dyn Any + Send>| {
   |                               ^^^ help: consider prefixing with an underscore: `_err`

playground

My real problem is not about going out of bounds of the vector, it's about catching the panic from par_iter if I don't know it will panic or not. My goal is to collect the result and move forward with stuff which didn't panic.

Upvotes: 2

Views: 1184

Answers (2)

Maria Dubyaga
Maria Dubyaga

Reputation: 41

My question was answered in the Rust User's Forum using ::std::panic::catch_unwind():

use ::rayon::prelude::*;

trait Animal: Sync + Send {
    fn vector(self: &'_ Self) -> Vec<i32>;
    fn name(self: &'_ Self) -> String;
}

struct Cat {
    name: &'static str,
    vector: Vec<i32>,
}

impl Animal for Cat {
    fn vector(self: &'_ Self) -> Vec<i32> {
        self.vector.clone()
    }

    fn name(self: &'_ Self) -> String {
        self.name.to_string()
    }
}

struct Dog {
    name: &'static str,
    vector: Vec<i32>,
}

impl Animal for Dog {
    fn vector(self: &'_ Self) -> Vec<i32> {
        self.vector.clone()
    }

    fn name(self: &'_ Self) -> String {
        self.name.to_string()
    }
}

struct Pig {
    name: &'static str,
    vector: Vec<i32>,
}

impl Animal for Pig {
    fn vector(self: &'_ Self) -> Vec<i32> {
        self.vector.clone()
    }

    fn name(self: &'_ Self) -> String {
        self.name.to_string()
    }
}

fn main() {
    ::rayon::ThreadPoolBuilder::new()
        .num_threads(2)
        // .panic_handler(move |_: Box<dyn Any + Send>| println!("hello"))
        .build_global()
        .unwrap();

    match ::std::panic::catch_unwind(move || {
        let dog: Dog = Dog {
            name: "Dog",
            vector: vec![1, 2, 3, 4, 5],
        };
        let cat: Cat = Cat {
            name: "Cat",
            vector: vec![1, 2, 3],
        };
        let pig: Pig = Pig {
            name: "Pig",
            vector: vec![1, 2, 3, 4, 5],
        };
        let v: Vec<Box<dyn Animal>> = vec![Box::new(cat), Box::new(dog), Box::new(pig)];
        let total = v
            .par_iter()
            .map(|x| {
                let vector_4 = x.vector()[4].clone();
                println!("{} vector[4] is {:?}", &x.name(), vector_4);
                vector_4
            })
            .collect::<Vec<(i32)>>();
        let sum: i32 = total.iter().sum();
        println!("sum: {}", sum);
    }) {
        Ok(()) => (),
        Err(err) => {
            let err_msg = match (err.downcast_ref(), err.downcast_ref::<String>()) {
                (Some(&s), _) => s,
                (_, Some(s)) => &**s,
                _ => "<No panic message>",
            };
            eprintln!("Rayon panicked: {}", err_msg);
        }
    }
    println!("This code is always reached");
}

Upvotes: 2

phimuemue
phimuemue

Reputation: 35983

Try using get (it returns Some(element) if there is an element at index 4, None otherwise):

let total = v.par_iter().map(|x| {
    println!("{} vector[4] is {:?}", &x.name(), &x.vector().get(4));
    x.vector().get(4).map(|x| x.clone())
}).collect::<Vec<Option<i32>>>();

Then, total will contain Some(n) where the respective element was present and None otherwise.

Upvotes: 2

Related Questions