Option <i32>展开安全吗?

时间:2018-09-12 17:23:56

标签: rust ffi

我正在实现一个C库的包装,该包装接受回调,并且该回调将在Rust中实现。鉴于panicking in Rust when calling from C is undefined behavior,我想在潜在的Rust恐慌进入C之前对其进行捕捉。

我一直在阅读有关std::panic::catch_unwind的信息。包装器对性能敏感,我希望避免使用类似Mutex的类型。我希望将结果保存在Option<i32>中,并在没有恐慌的情况下将其设置为Some(value)None将指示该函数未成功执行,因此必定发生了恐慌。

Option<i32>放心安全吗?如果没有,在什么情况下会出现问题?我可以将其包装在std::panic::AssertUnwindSafe中吗?

这里是一个示例,其中我使用AssertUnwindSafe包装了整个闭包。

use std::panic::{self, AssertUnwindSafe};

fn random_function_that_might_panic(a: i32) -> i32 {
    if a == 42 {
        panic!("did you forget a towel?");
    }
    a * 2
}

fn do_not_panic(a: i32) {
    let mut result = None;
    let unwind_state = panic::catch_unwind(AssertUnwindSafe(|| {
        result = Some(random_function_that_might_panic(a)); // get result, but this could panic
    }));
    match unwind_state {
        Ok(()) => {
            match result {
                Some(value) => {
                    println!("Result: {:?}", value);
                }
                None => {
                    // this should never happen...
                    println!("No result but no panic?");
                }
            }
        }
        Err(e) => {
            println!("caught panic: {:?}", e);
        }
    }
}

fn main() {
    do_not_panic(1);
    do_not_panic(2);
    do_not_panic(3);
    do_not_panic(42);
}

(请参见playground上的内容。)

我不知道如何仅将Option<i32>包装在AssertUnwindSafe中,因此在这里包装了整个闭包。我怎么只包裹Option<i32>

1 个答案:

答案 0 :(得分:5)

  

Option<i32>放心安全吗?

是的。当您可以向编译器提问时,没有理由向人类提出这个问题:

fn implements<T: std::panic::UnwindSafe>() {}

fn main() {
    implements::<Option<i32>>();
}

您的 real 问题应该是:

  

&mut Option<i32>放心安全吗?

不是,但是您可能已经知道了。我猜想您在添加AssertUnwindSafe之前就遇到了此编译器错误,该错误提示您不安全:

error[E0277]: the trait bound `&mut std::option::Option<i32>: std::panic::UnwindSafe` is not satisfied in `[closure@src/main.rs:12:44: 14:6 result:&mut std::option::Option<i32>, a:&i32]`
  --> src/main.rs:12:24
   |
12 |     let unwind_state = panic::catch_unwind(|| {
   |                        ^^^^^^^^^^^^^^^^^^^ the type &mut std::option::Option<i32> may not be safely transferred across an unwind boundary
   |
   = help: within `[closure@src/main.rs:12:44: 14:6 result:&mut std::option::Option<i32>, a:&i32]`, the trait `std::panic::UnwindSafe` is not implemented for `&mut std::option::Option<i32>`
   = note: required because it appears within the type `[closure@src/main.rs:12:44: 14:6 result:&mut std::option::Option<i32>, a:&i32]`
   = note: required by `std::panic::catch_unwind`

我会这样写你的代码,因为它值得:

fn do_not_panic(a: i32) {
    let result = panic::catch_unwind(|| random_function_that_might_panic(a)).ok();

    match result {
        Some(value) => {
            println!("Result: {:?}", value);
        }
        None => {
            println!("caught panic");
        }
    }
}

没有可变变量,没有额外的嵌套,也没有“永远不应该发生”的注释。

另请参阅: