Восстановление из `panic!` В другом потоке

Я знаю, что в Rust нет try/catch, и вы не можете выбросить скользящее сохранение из потока, который в настоящее время впадает в панику.

Я знаю, что вы не должны создавать и обрабатывать такие ошибки. Это, например, сакэ.

Тем не менее, мне интересно, какой лучший способ выздороветь от паники. Это то, что у меня есть сейчас:

use std::thread;

fn main() {
    println!("Hello, world!");

    let h = thread::spawn(|| {
        thread::sleep_ms(1000);
        panic!("boom");
    });

    let r = h.join();
    match r {
        Ok(r) => println!("All is well! {:?}", r),
        Err(e) => println!("Got an error! {:?}", e)
    }

    println!("Exiting main!");
}

Есть ли лучший способ обработки ошибок из других потоков? Есть ли способ захватить сообщение о панике? Это говорит только о том, что ошибка имеет тип Any. Спасибо!

Ответ 1

Отложив в сторону "вы должны использовать Result, где это возможно", да, это в основном то, как вы попадаете в панику в Rust. Имейте в виду, что "восстановить", возможно, не самый лучший способ сформулировать это в Rust. Вы действительно не выздоравливаете от паники в Rust, вы их изолируете, а затем обнаруживаете. Нет On Error Resume Next: P.

Тем не менее, к вашему примеру есть две вещи. Во-первых, как получить сообщение о панике. Главное наблюдение заключается в том, что Any, чтобы быть использованным, должно быть явно опущено к конкретному конкретному типу, который он содержит. В этом случае, поскольку сообщение о панике представляет собой &'static str, вам нужно сделать снимок.

Во-вторых, есть новый API в ночном названии catch_panic, который позволяет изолировать панику без необходимости запуска потока. Тем не менее, он имеет те же ограничения, что и порождение нового потока: вы не можете передать ссылку 'static через границу изоляции. Обратите внимание, что это нестабильное дополнение; пока нет гарантий относительно стабильности, и вам понадобится ночной компилятор для доступа к нему.

Вот пример, который показывает оба из них. Вы также можете запустить это в руке ржавчины.

#![feature(catch_panic)]

use std::thread;

fn main() {
    println!("Hello, world!");

    let h = thread::spawn(|| {
        thread::sleep_ms(500);
        panic!("boom");
    });

    let r = h.join();
    handle(r);

    let r = thread::catch_panic(|| {
        thread::sleep_ms(500);
        panic!(String::from("boom again!"));
    });

    handle(r);

    println!("Exiting main!");
}

fn handle(r: thread::Result<()>) {
    match r {
        Ok(r) => println!("All is well! {:?}", r),
        Err(e) => {
            if let Some(e) = e.downcast_ref::<&'static str>() {
                println!("Got an error: {}", e);
            } else {
                println!("Got an unknown error: {:?}", e);
            }
        }
    }
}