Remove cfg min_const_generics made unnecessary by MSRV bump.
This commit is contained in:
parent
f7bd6ce224
commit
eb04f54683
2
build.rs
2
build.rs
|
@ -46,7 +46,7 @@ fn configure_pyo3() -> Result<()> {
|
||||||
|
|
||||||
ensure_auto_initialize_ok(interpreter_config)?;
|
ensure_auto_initialize_ok(interpreter_config)?;
|
||||||
|
|
||||||
// Emit cfgs like `addr_of` and `min_const_generics`
|
// Emit cfgs like `thread_local_const_init`
|
||||||
print_feature_cfgs();
|
print_feature_cfgs();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -142,11 +142,6 @@ pub fn print_feature_cfgs() {
|
||||||
|
|
||||||
let rustc_minor_version = rustc_minor_version().unwrap_or(0);
|
let rustc_minor_version = rustc_minor_version().unwrap_or(0);
|
||||||
|
|
||||||
// Enable use of const generics on Rust 1.51 and greater
|
|
||||||
if rustc_minor_version >= 51 {
|
|
||||||
println!("cargo:rustc-cfg=min_const_generics");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable use of std::ptr::addr_of! on Rust 1.51 and greater
|
// Enable use of std::ptr::addr_of! on Rust 1.51 and greater
|
||||||
if rustc_minor_version >= 51 {
|
if rustc_minor_version >= 51 {
|
||||||
println!("cargo:rustc-cfg=addr_of");
|
println!("cargo:rustc-cfg=addr_of");
|
||||||
|
|
|
@ -100,7 +100,7 @@ fn configure_pyo3() -> Result<()> {
|
||||||
println!("{}", line);
|
println!("{}", line);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit cfgs like `addr_of` and `min_const_generics`
|
// Emit cfgs like `thread_local_const_init`
|
||||||
print_feature_cfgs();
|
print_feature_cfgs();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,308 +1,118 @@
|
||||||
|
use crate::conversion::{AsPyPointer, IntoPyPointer};
|
||||||
|
use crate::types::PySequence;
|
||||||
use crate::{exceptions, PyErr};
|
use crate::{exceptions, PyErr};
|
||||||
|
use crate::{
|
||||||
|
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python, ToPyObject,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(min_const_generics)]
|
impl<T, const N: usize> IntoPy<PyObject> for [T; N]
|
||||||
mod min_const_generics {
|
where
|
||||||
use super::invalid_sequence_length;
|
T: IntoPy<PyObject>,
|
||||||
use crate::conversion::{AsPyPointer, IntoPyPointer};
|
{
|
||||||
use crate::types::PySequence;
|
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||||
use crate::{
|
|
||||||
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python,
|
|
||||||
ToPyObject,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl<T, const N: usize> IntoPy<PyObject> for [T; N]
|
|
||||||
where
|
|
||||||
T: IntoPy<PyObject>,
|
|
||||||
{
|
|
||||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
|
||||||
unsafe {
|
|
||||||
#[allow(deprecated)] // we're not on edition 2021 yet
|
|
||||||
let elements = std::array::IntoIter::new(self);
|
|
||||||
let len = N as ffi::Py_ssize_t;
|
|
||||||
|
|
||||||
let ptr = ffi::PyList_New(len);
|
|
||||||
|
|
||||||
// We create the `Py` pointer here for two reasons:
|
|
||||||
// - panics if the ptr is null
|
|
||||||
// - its Drop cleans up the list if user code panics.
|
|
||||||
let list: Py<PyAny> = Py::from_owned_ptr(py, ptr);
|
|
||||||
|
|
||||||
for (i, obj) in (0..len).zip(elements) {
|
|
||||||
let obj = obj.into_py(py).into_ptr();
|
|
||||||
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
ffi::PyList_SET_ITEM(ptr, i, obj);
|
|
||||||
#[cfg(Py_LIMITED_API)]
|
|
||||||
ffi::PyList_SetItem(ptr, i, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
list
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> ToPyObject for [T; N]
|
|
||||||
where
|
|
||||||
T: ToPyObject,
|
|
||||||
{
|
|
||||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
|
||||||
self.as_ref().to_object(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T, const N: usize> FromPyObject<'a> for [T; N]
|
|
||||||
where
|
|
||||||
T: FromPyObject<'a>,
|
|
||||||
{
|
|
||||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
|
||||||
create_array_from_obj(obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_array_from_obj<'s, T, const N: usize>(obj: &'s PyAny) -> PyResult<[T; N]>
|
|
||||||
where
|
|
||||||
T: FromPyObject<'s>,
|
|
||||||
{
|
|
||||||
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
|
|
||||||
// to support this function and if not, we will only fail extraction safely.
|
|
||||||
let seq: &PySequence = unsafe {
|
|
||||||
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
|
|
||||||
obj.downcast_unchecked()
|
|
||||||
} else {
|
|
||||||
return Err(PyDowncastError::new(obj, "Sequence").into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let seq_len = seq.len()?;
|
|
||||||
if seq_len != N {
|
|
||||||
return Err(invalid_sequence_length(N, seq_len));
|
|
||||||
}
|
|
||||||
array_try_from_fn(|idx| seq.get_item(idx).and_then(PyAny::extract))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO use std::array::try_from_fn, if that stabilises:
|
|
||||||
// (https://github.com/rust-lang/rust/pull/75644)
|
|
||||||
fn array_try_from_fn<E, F, T, const N: usize>(mut cb: F) -> Result<[T; N], E>
|
|
||||||
where
|
|
||||||
F: FnMut(usize) -> Result<T, E>,
|
|
||||||
{
|
|
||||||
// Helper to safely create arrays since the standard library doesn't
|
|
||||||
// provide one yet. Shouldn't be necessary in the future.
|
|
||||||
struct ArrayGuard<T, const N: usize> {
|
|
||||||
dst: *mut T,
|
|
||||||
initialized: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Drop for ArrayGuard<T, N> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
debug_assert!(self.initialized <= N);
|
|
||||||
let initialized_part =
|
|
||||||
core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
|
|
||||||
unsafe {
|
|
||||||
core::ptr::drop_in_place(initialized_part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [MaybeUninit<T>; N] would be "nicer" but is actually difficult to create - there are nightly
|
|
||||||
// APIs which would make this easier.
|
|
||||||
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
|
|
||||||
let mut guard: ArrayGuard<T, N> = ArrayGuard {
|
|
||||||
dst: array.as_mut_ptr() as _,
|
|
||||||
initialized: 0,
|
|
||||||
};
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut value_ptr = array.as_mut_ptr() as *mut T;
|
#[allow(deprecated)] // we're not on edition 2021 yet
|
||||||
for i in 0..N {
|
let elements = std::array::IntoIter::new(self);
|
||||||
core::ptr::write(value_ptr, cb(i)?);
|
let len = N as ffi::Py_ssize_t;
|
||||||
value_ptr = value_ptr.offset(1);
|
|
||||||
guard.initialized += 1;
|
let ptr = ffi::PyList_New(len);
|
||||||
|
|
||||||
|
// We create the `Py` pointer here for two reasons:
|
||||||
|
// - panics if the ptr is null
|
||||||
|
// - its Drop cleans up the list if user code panics.
|
||||||
|
let list: Py<PyAny> = Py::from_owned_ptr(py, ptr);
|
||||||
|
|
||||||
|
for (i, obj) in (0..len).zip(elements) {
|
||||||
|
let obj = obj.into_py(py).into_ptr();
|
||||||
|
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
ffi::PyList_SET_ITEM(ptr, i, obj);
|
||||||
|
#[cfg(Py_LIMITED_API)]
|
||||||
|
ffi::PyList_SetItem(ptr, i, obj);
|
||||||
}
|
}
|
||||||
core::mem::forget(guard);
|
|
||||||
Ok(array.assume_init())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
list
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::{
|
|
||||||
panic,
|
|
||||||
sync::atomic::{AtomicUsize, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn array_try_from_fn() {
|
|
||||||
static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
struct CountDrop;
|
|
||||||
impl Drop for CountDrop {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let _ = catch_unwind_silent(move || {
|
|
||||||
let _: Result<[CountDrop; 4], ()> = super::array_try_from_fn(|idx| {
|
|
||||||
#[allow(clippy::manual_assert)]
|
|
||||||
if idx == 2 {
|
|
||||||
panic!("peek a boo");
|
|
||||||
}
|
|
||||||
Ok(CountDrop)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
assert_eq!(DROP_COUNTER.load(Ordering::SeqCst), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_extract_bytearray_to_array() {
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
let v: [u8; 33] = py
|
|
||||||
.eval(
|
|
||||||
"bytearray(b'abcabcabcabcabcabcabcabcabcabcabc')",
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
.extract()
|
|
||||||
.unwrap();
|
|
||||||
assert!(&v == b"abcabcabcabcabcabcabcabcabcabcabc");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/59211505
|
|
||||||
fn catch_unwind_silent<F, R>(f: F) -> std::thread::Result<R>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> R + panic::UnwindSafe,
|
|
||||||
{
|
|
||||||
let prev_hook = panic::take_hook();
|
|
||||||
panic::set_hook(Box::new(|_| {}));
|
|
||||||
let result = panic::catch_unwind(f);
|
|
||||||
panic::set_hook(prev_hook);
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(min_const_generics))]
|
impl<T, const N: usize> ToPyObject for [T; N]
|
||||||
mod array_impls {
|
where
|
||||||
use super::invalid_sequence_length;
|
T: ToPyObject,
|
||||||
use crate::conversion::{AsPyPointer, IntoPyPointer};
|
{
|
||||||
use crate::types::PySequence;
|
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||||
use crate::{
|
self.as_ref().to_object(py)
|
||||||
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python,
|
}
|
||||||
ToPyObject,
|
}
|
||||||
|
|
||||||
|
impl<'a, T, const N: usize> FromPyObject<'a> for [T; N]
|
||||||
|
where
|
||||||
|
T: FromPyObject<'a>,
|
||||||
|
{
|
||||||
|
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||||
|
create_array_from_obj(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_array_from_obj<'s, T, const N: usize>(obj: &'s PyAny) -> PyResult<[T; N]>
|
||||||
|
where
|
||||||
|
T: FromPyObject<'s>,
|
||||||
|
{
|
||||||
|
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
|
||||||
|
// to support this function and if not, we will only fail extraction safely.
|
||||||
|
let seq: &PySequence = unsafe {
|
||||||
|
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
|
||||||
|
obj.downcast_unchecked()
|
||||||
|
} else {
|
||||||
|
return Err(PyDowncastError::new(obj, "Sequence").into());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
use std::mem::{transmute_copy, ManuallyDrop};
|
let seq_len = seq.len()?;
|
||||||
|
if seq_len != N {
|
||||||
|
return Err(invalid_sequence_length(N, seq_len));
|
||||||
|
}
|
||||||
|
array_try_from_fn(|idx| seq.get_item(idx).and_then(PyAny::extract))
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! array_impls {
|
// TODO use std::array::try_from_fn, if that stabilises:
|
||||||
($($N:expr),+) => {
|
// (https://github.com/rust-lang/rust/pull/75644)
|
||||||
$(
|
fn array_try_from_fn<E, F, T, const N: usize>(mut cb: F) -> Result<[T; N], E>
|
||||||
impl<T> IntoPy<PyObject> for [T; $N]
|
where
|
||||||
where
|
F: FnMut(usize) -> Result<T, E>,
|
||||||
T: IntoPy<PyObject>
|
{
|
||||||
{
|
// Helper to safely create arrays since the standard library doesn't
|
||||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
// provide one yet. Shouldn't be necessary in the future.
|
||||||
|
struct ArrayGuard<T, const N: usize> {
|
||||||
|
dst: *mut T,
|
||||||
|
initialized: usize,
|
||||||
|
}
|
||||||
|
|
||||||
struct ArrayGuard<T> {
|
impl<T, const N: usize> Drop for ArrayGuard<T, N> {
|
||||||
elements: [ManuallyDrop<T>; $N],
|
fn drop(&mut self) {
|
||||||
start: usize,
|
debug_assert!(self.initialized <= N);
|
||||||
}
|
let initialized_part = core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
|
||||||
|
unsafe {
|
||||||
impl<T> Drop for ArrayGuard<T> {
|
core::ptr::drop_in_place(initialized_part);
|
||||||
fn drop(&mut self) {
|
}
|
||||||
unsafe {
|
|
||||||
let needs_drop = self.elements.get_mut(self.start..).unwrap();
|
|
||||||
for item in needs_drop {
|
|
||||||
ManuallyDrop::drop(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let ptr = ffi::PyList_New($N as ffi::Py_ssize_t);
|
|
||||||
|
|
||||||
// We create the `Py` pointer here for two reasons:
|
|
||||||
// - panics if the ptr is null
|
|
||||||
// - its Drop cleans up the list if user code panics.
|
|
||||||
let list: Py<PyAny> = Py::from_owned_ptr(py, ptr);
|
|
||||||
|
|
||||||
let slf = ManuallyDrop::new(self);
|
|
||||||
|
|
||||||
let mut guard = ArrayGuard {
|
|
||||||
// the transmute size check is _very_ dumb around generics
|
|
||||||
elements: transmute_copy(&slf),
|
|
||||||
start: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in 0..$N {
|
|
||||||
let obj: T = ManuallyDrop::take(&mut guard.elements[i]);
|
|
||||||
guard.start += 1;
|
|
||||||
|
|
||||||
let obj = obj.into_py(py).into_ptr();
|
|
||||||
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
ffi::PyList_SET_ITEM(ptr, i as ffi::Py_ssize_t, obj);
|
|
||||||
#[cfg(Py_LIMITED_API)]
|
|
||||||
ffi::PyList_SetItem(ptr, i as ffi::Py_ssize_t, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mem::forget(guard);
|
|
||||||
|
|
||||||
list
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ToPyObject for [T; $N]
|
|
||||||
where
|
|
||||||
T: ToPyObject,
|
|
||||||
{
|
|
||||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
|
||||||
self.as_ref().to_object(py)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> FromPyObject<'a> for [T; $N]
|
|
||||||
where
|
|
||||||
T: Copy + Default + FromPyObject<'a>,
|
|
||||||
{
|
|
||||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
|
||||||
let mut array = [T::default(); $N];
|
|
||||||
extract_sequence_into_slice(obj, &mut array)?;
|
|
||||||
Ok(array)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(min_const_generics))]
|
// [MaybeUninit<T>; N] would be "nicer" but is actually difficult to create - there are nightly
|
||||||
array_impls!(
|
// APIs which would make this easier.
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
|
||||||
25, 26, 27, 28, 29, 30, 31, 32
|
let mut guard: ArrayGuard<T, N> = ArrayGuard {
|
||||||
);
|
dst: array.as_mut_ptr() as _,
|
||||||
|
initialized: 0,
|
||||||
#[cfg(not(min_const_generics))]
|
};
|
||||||
fn extract_sequence_into_slice<'s, T>(obj: &'s PyAny, slice: &mut [T]) -> PyResult<()>
|
unsafe {
|
||||||
where
|
let mut value_ptr = array.as_mut_ptr() as *mut T;
|
||||||
T: FromPyObject<'s>,
|
for i in 0..N {
|
||||||
{
|
core::ptr::write(value_ptr, cb(i)?);
|
||||||
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
|
value_ptr = value_ptr.offset(1);
|
||||||
// to support this function and if not, we will only fail extraction safely.
|
guard.initialized += 1;
|
||||||
let seq: &PySequence = unsafe {
|
|
||||||
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
|
|
||||||
obj.downcast_unchecked()
|
|
||||||
} else {
|
|
||||||
return Err(PyDowncastError::new(obj, "Sequence").into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let seq_len = seq.len()?;
|
|
||||||
if seq_len != slice.len() {
|
|
||||||
return Err(invalid_sequence_length(slice.len(), seq_len));
|
|
||||||
}
|
}
|
||||||
for (value, item) in slice.iter_mut().zip(seq.iter()?) {
|
core::mem::forget(guard);
|
||||||
*value = item?.extract::<T>()?;
|
Ok(array.assume_init())
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,8 +125,50 @@ fn invalid_sequence_length(expected: usize, actual: usize) -> PyErr {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::{
|
||||||
|
panic,
|
||||||
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{types::PyList, IntoPy, PyResult, Python, ToPyObject};
|
use crate::{types::PyList, IntoPy, PyResult, Python, ToPyObject};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array_try_from_fn() {
|
||||||
|
static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
struct CountDrop;
|
||||||
|
impl Drop for CountDrop {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = catch_unwind_silent(move || {
|
||||||
|
let _: Result<[CountDrop; 4], ()> = super::array_try_from_fn(|idx| {
|
||||||
|
#[allow(clippy::manual_assert)]
|
||||||
|
if idx == 2 {
|
||||||
|
panic!("peek a boo");
|
||||||
|
}
|
||||||
|
Ok(CountDrop)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
assert_eq!(DROP_COUNTER.load(Ordering::SeqCst), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_extract_bytearray_to_array() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let v: [u8; 33] = py
|
||||||
|
.eval(
|
||||||
|
"bytearray(b'abcabcabcabcabcabcabcabcabcabcabc')",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.extract()
|
||||||
|
.unwrap();
|
||||||
|
assert!(&v == b"abcabcabcabcabcabcabcabcabcabcabc");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extract_small_bytearray_to_array() {
|
fn test_extract_small_bytearray_to_array() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
|
@ -390,4 +242,16 @@ mod tests {
|
||||||
let _cell: &crate::PyCell<Foo> = list.get_item(4).unwrap().extract().unwrap();
|
let _cell: &crate::PyCell<Foo> = list.get_item(4).unwrap().extract().unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/59211505
|
||||||
|
fn catch_unwind_silent<F, R>(f: F) -> std::thread::Result<R>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> R + panic::UnwindSafe,
|
||||||
|
{
|
||||||
|
let prev_hook = panic::take_hook();
|
||||||
|
panic::set_hook(Box::new(|_| {}));
|
||||||
|
let result = panic::catch_unwind(f);
|
||||||
|
panic::set_hook(prev_hook);
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue