-
-
Save rust-play/8106a2e9d323bab7fd5884b124ce201e to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//////////////////////////////////////////////////////////////////////////////// | |
// This section from https://docs.rs/existential/0.1.1/src/existential/lib.rs.html#106-113 | |
pub trait TyConFor<'a> { | |
type Applied; | |
} | |
pub type Apply<'a, C> = <C as TyConFor<'a>>::Applied; | |
pub trait TyCon: for<'a> TyConFor<'a> {} | |
impl<C: ?Sized> TyCon for C where C: for<'a> TyConFor<'a> {} | |
//////////////////////////////////////////////////////////////////////////////// | |
pub mod scoped_existential { | |
use super::*; | |
use std::mem; | |
use std::sync::{Arc, RwLock, TryLockError, Weak}; | |
pub fn scope<'scope, F, R>(f: F) -> R | |
where | |
F: FnOnce(&mut Scope<'scope>) -> R, | |
{ | |
f(&mut Scope { | |
data: <_>::default(), | |
lock: <_>::default(), | |
}) | |
} | |
pub struct Scope<'scope> { | |
data: Vec<Arc<dyn Send + Sync + 'scope>>, | |
lock: Arc<RwLock<()>>, | |
} | |
impl<C: TyCon> Clone for ScopedExistential<C> { | |
fn clone(&self) -> Self { | |
Self { | |
payload: self.payload.clone(), | |
lock: self.lock.clone(), | |
} | |
} | |
} | |
// TODO: Send/Sync implementation is slightly inaccurate (due to 'static) | |
pub struct ScopedExistential<C: TyCon> { | |
payload: Weak<Apply<'static, C>>, | |
lock: Arc<RwLock<()>>, | |
} | |
impl<C: TyCon> ScopedExistential<C> { | |
pub fn with_upgrade<F, R>(&self, f: F) -> R | |
where | |
F: FnOnce(Option<&Apply<'_, C>>) -> R, | |
{ | |
match self.lock.try_read() { | |
Err(TryLockError::Poisoned(_)) => unreachable!(), | |
Err(TryLockError::WouldBlock) => f(None), | |
Ok(guard) => match self.payload.upgrade() { | |
None => { | |
drop(guard); | |
f(None) | |
} | |
Some(payload) => { | |
let r = f(Some(&payload)); | |
drop(payload); | |
drop(guard); | |
r | |
} | |
}, | |
} | |
} | |
} | |
impl<'scope> Scope<'scope> { | |
/// erases lifetime "'a" from a type Arc<Foo<'a>> | |
pub fn erase<'a: 'scope, C: TyCon>(&mut self, val: Apply<'a, C>) -> ScopedExistential<C> | |
where | |
C: 'scope, | |
Apply<'a, C>: Send + Sync, | |
{ | |
let val = Arc::new(val); | |
let weak = Arc::downgrade(&val); | |
self.data.push(val); | |
let payload = | |
unsafe { mem::transmute::<Weak<Apply<'a, C>>, Weak<Apply<'static, C>>>(weak) }; | |
ScopedExistential { | |
payload, | |
lock: self.lock.clone(), | |
} | |
} | |
} | |
impl<'scope> Drop for Scope<'scope> { | |
fn drop(&mut self) { | |
let guard = self.lock.write().unwrap(); | |
self.data.clear(); | |
drop(guard); | |
} | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// Useage without further unsafe code... | |
use scoped_existential::{scope, Scope}; | |
use std::marker::PhantomData; | |
use std::sync::{Arc, Mutex}; | |
// assume scoped_existential was a different crate, so we use an extension trait | |
trait ExtendMut<'scope> { | |
fn extend_mut<'long, A, R, E, F>( | |
&mut self, | |
error: E, | |
func: F, | |
) -> Box<dyn 'long + FnMut(A) -> R + Send> | |
where | |
'long: 'scope, | |
A: 'long, | |
R: 'long, | |
E: 'long + FnMut(A) -> R + Send, | |
F: 'scope + FnMut(A) -> R + Send; | |
} | |
impl<'scope> ExtendMut<'scope> for Scope<'scope> { | |
fn extend_mut<'long, A, R, E, F>( | |
&mut self, | |
mut error: E, | |
func: F, | |
) -> Box<dyn 'long + FnMut(A) -> R + Send> | |
where | |
'long: 'scope, | |
A: 'long, | |
R: 'long, | |
E: 'long + FnMut(A) -> R + Send, | |
F: 'scope + FnMut(A) -> R + Send, | |
{ | |
let func = Mutex::new(Box::new(func) as _); | |
struct BoxDynFnMutSend<A, R>(PhantomData<fn() -> (A, R)>); | |
impl<'a, A, R> TyConFor<'a> for BoxDynFnMutSend<A, R> { | |
type Applied = Mutex<Box<dyn 'a + FnMut(A) -> R + Send>>; | |
} | |
let func = self.erase::<BoxDynFnMutSend<A, R>>(func); | |
Box::new(move |a| { | |
func.with_upgrade(|maybe_func| match maybe_func { | |
None => error(a), | |
Some(func) => func.try_lock().unwrap()(a), | |
}) | |
}) | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
use std::sync::Mutex; | |
#[test] | |
fn test_in_scope() { | |
let errorstate: Arc<Mutex<i32>> = Arc::new(Mutex::new(0)); | |
let clone = errorstate.clone(); | |
let error = move |()| { | |
*clone.lock().unwrap() += 3; | |
}; | |
let mut i: Box<i32> = Box::new(0); | |
scope(|s| { | |
let mut extended = s.extend_mut(error, |()| { | |
*i += 2; | |
}); | |
extended(()); | |
}); | |
assert_eq!(*i, 2); | |
assert_eq!(*errorstate.lock().unwrap(), 0); | |
} | |
#[test] | |
fn test_out_of_scope() { | |
let errorstate: Arc<Mutex<i32>> = Arc::new(Mutex::new(0)); | |
let clone = errorstate.clone(); | |
let error = move |()| { | |
*clone.lock().unwrap() += 7; | |
}; | |
let mut i: Box<i32> = Box::new(0); | |
let mut extended = scope(|s| { | |
let extended = s.extend_mut(error, |()| { | |
*i += 5; | |
}); | |
extended | |
}); | |
extended(()); | |
drop(extended); | |
assert_eq!(*i, 0); | |
assert_eq!(*errorstate.lock().unwrap(), 7); | |
} | |
#[test] | |
fn test_out_of_scope_with_drop() { | |
let errorstate: Arc<Mutex<i32>> = Arc::new(Mutex::new(0)); | |
let clone = errorstate.clone(); | |
let error = move |()| { | |
*clone.lock().unwrap() += 13; | |
}; | |
let mut i: Box<i32> = Box::new(0); | |
let mut extended = scope(|s| { | |
let extended = s.extend_mut(error, |()| { | |
*i += 11; | |
}); | |
extended | |
}); | |
assert_eq!(*i, 0); | |
drop(i); | |
extended(()); | |
assert_eq!(*errorstate.lock().unwrap(), 13); | |
} | |
#[test] | |
fn test_automatic_dropping() { | |
let dropcheck: Arc<Mutex<i32>> = Arc::new(Mutex::new(0)); | |
let extended = { | |
let extended = scope(|s| { | |
struct MonitorDrop(Arc<Mutex<i32>>); | |
impl Drop for MonitorDrop { | |
fn drop(&mut self) { | |
*self.0.lock().unwrap() += 1; | |
} | |
} | |
let r = MonitorDrop(dropcheck.clone()); | |
let f = move |()| { | |
let _ = &r; | |
}; | |
let extended = s.extend_mut(|()| (), f); | |
assert_eq!(*dropcheck.lock().unwrap(), 0); | |
extended | |
}); | |
assert_eq!(*dropcheck.lock().unwrap(), 1); | |
extended | |
}; | |
drop(extended); | |
} | |
} | |
use std::sync::mpsc; | |
use std::thread; | |
use std::time::Duration; | |
fn _main() { | |
let string = "Hello World".to_owned(); | |
let (tx1, rx1) = mpsc::channel::<()>(); | |
let (tx2, rx2) = mpsc::channel::<()>(); | |
let handle = scope(|s| { | |
let string_ref = string.as_str(); | |
let mut f = s.extend_mut( | |
|()| (), | |
move |()| { | |
tx1.send(()).unwrap(); | |
// use timeout to avoid deadlock in case `scope` | |
// was correctly implemented | |
let _ = rx2.recv_timeout(Duration::from_secs(1)); // ignore timeout | |
println!("{}", string_ref); | |
}, | |
); | |
let handle = thread::spawn(move || { | |
f(()); | |
}); | |
rx1.recv().unwrap(); | |
handle | |
}); | |
drop(string); | |
println!("DROPPED"); | |
let _ = tx2.send(()); // ignore disconnected | |
handle.join().unwrap(); | |
} | |
use std::mem; | |
fn main() { | |
scope(|s| { | |
replace_with::replace_with_or_abort(s, |s| { | |
let string = "Hello World".to_owned(); | |
let string_ref = &string; | |
let mut s = s; // variance! | |
let mut f = s.extend_mut( | |
|()| (), | |
|()| { | |
println!("{}", string); | |
}, | |
); | |
mem::forget(s); | |
drop(string); | |
f(()); // accesses the string after free | |
panic!(); // results in abort | |
}) | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment