Does anyone have a workable solution for bubbling Result<T,E> errors out of map closures that need to return a reference? For example, the map closure of RefCell
struct MyStruct {
contents: RefCell<Box<dyn SomeTrait>>
}
impl MyStruct {
pub fn access<T>(&self) -> Result<Ref<T>, String>
where T: Any + 'static,
{
let my_ref = self.contents.borrow();
let mapped_ref = Ref::map(my_ref, |my_ref| {
my_ref.expensive_function_that_may_fail::<T>()?
//Obviously the ? doesn't work inside the closure.
// But I don't want to unwrap() the result because the function often fails
// And the cost of the function means that preflighting it isn't an option.
});
Ok(mapped_ref)
}
}
I see std::Cell::Ref::filter_map but it's unstable, and only takes an option (as opposed to a Result)
What's the best idiomatic solution that people use in this case?
Let me ask the question slightly differently: Is there any reason this is a bad idea?
struct MyStruct {
contents: RefCell<Box<dyn SomeTrait>>
}
impl MyStruct {
pub fn access<T>(&self) -> Result<Ref<T>, String>
where T: Any + 'static,
{
let my_ref = self.contents.borrow();
let function_out = my_ref.expensive_function_that_may_fail::<T>()?
//Safety Note: transmuted can't outlive of my_ref because we map my_ref into it
let transmuted = unsafe { &*((&*function_out) as *const dyn Any as *const T) };
Ok(Ref::map(my_ref, |_| {
transmuted
}));
}
}
You can do this without any unsafe by passing the error out of the closure in a mutable Option variable, then returning an arbitrary &'static reference to satisfy Ref::map. Then if the variable contains an error, you ignore the reference and return Err.
It's a workaround for not having Ref::filter_map, and it's inelegant, but it works.
Yes. You are using unsafe to circumvent the borrow checker. This is recognizably an anti-pattern, and it usually leads to unsoundness and Undefined Behavior.
You shouldn't use unsafe for "fixing" compiler errors, and it definitely shouldn't be the first tool you reach for. You should try really hard to solve problems using safe language and library features instead.
The exceptions are when you want to perform FFI calls to C, or when you find a case where micro-optimizing a data structure requires you to manually manage memory. The latter, however, is exceedingly rare, so as a first-order approximation, the advice is "don't use unsafe unless you need FFI".