1 unstable release
Uses old Rust 2015
0.1.0 | Feb 8, 2022 |
---|
#6 in #workaround
12KB
Polonius_workaround
This crate provides simple and logical safe api over the unsafe workarounds for borrow checker limitations that will be solved by Polonius.
License
MIT
lib.rs
:
This crate provides api to solve borrow checker errors caused by limitation of current rust borrow checked. There exists next version of rust borrow checker called Polonius that is supposed to remove such limitations, but it is permanently unstable with no current plans for stabilization. And quite often the only way to work around those limitations on stable rust without some kind of regression is with unsafe code. But it is annoying to be required to use unsafe for such simple things, and it is too easy to get wrong, not to mention it is just delayed bomb waiting for some uncareful refactoring to release it.
All functionality is provided via PoloniusExt
extension trait.
It has 3 methods:
try_get_with
/try_get_mut_with
work for simple cases where you need to return shared/mutable reference respectively. It should just work and in most cases that is enough. But sometimes you need to return not a reference but some another type that contains a reference. Thats when you needtry_get_with2
It allows you to return any type with reference inside, but due to rust type inference bugs around HRTBs it is a bit annoying to use. See its docs for more details.
As far as author knows it allows to solve any king of borrow checker issues that would be solved by Polonius. Although you still need to be confident enough in Rust to know that your code is actually correct and just not accepted by current borrow checker. So although with this crate you do not actually need Polonius anymore, it is still a nice thing to have in general.
The code compiles since Rust 1.0 but
but try_get_with2
actually works only since Rust 1.41.
And here is a real example that you couldn't work around without significant performance regression or unsafe.
trait LinkedListExt<T> {
fn find_or_create<F, C>(&mut self, predicate: F, create: C) -> &mut T
where
F: FnMut(&T) -> bool,
C: FnMut() -> T;
}
impl<T: 'static> LinkedListExt<T> for LinkedList<T> {
fn find_or_create<F, C>(&mut self, mut predicate: F, mut create: C) -> &mut T
where
F: FnMut(&T) -> bool,
C: FnMut() -> T,
{
if let Some(x) = self.iter_mut().find(|e| predicate(&*e)){
return x;
}
self.push_back(create());
self.back_mut().unwrap()
}
}
Now with this crate you just wrap two branches into a closures then call try_get_mut_with
and voila - it works
impl<T: 'static> LinkedListExt<T> for LinkedList<T> {
fn find_or_create<F, C>(&mut self, mut predicate: F, mut create: C) -> &mut T
where
F: FnMut(&T) -> bool,
C: FnMut() -> T,
{
self.try_get_mut_with(|x| x.iter_mut().find(|e| predicate(&*e)))
.unwrap_or_else(|x| {
x.push_back(create());
x.back_mut().unwrap()
})
}
}