Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@darconeous
Forked from rust-play/playground.rs
Last active July 2, 2019 01:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darconeous/a37f2569117eb7e81357a3f62d841528 to your computer and use it in GitHub Desktop.
Save darconeous/a37f2569117eb7e81357a3f62d841528 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
use futures::prelude::*;
use pin_utils::unsafe_pinned;
use std::ops::Deref;
use std::pin::Pin;
use std::sync::Arc;
/// A container for a single object with lifetime that is bound to that of an `Arc`.
/// This is useful for passing around boxed futures that have a lifetime that is limited
/// to that of the object that created it.
///
/// For example, the following does not compile:
///
/// ```compile_fail
/// use futures::{future::ready,prelude::*};
/// use std::sync::{Arc,Weak};
/// use splot_core::{ArcGuard,ArcGuardExt};
///
/// # // Note: This next line is needed because `LocalBoxFuture` isn't in the `futures` crate yet.
/// # pub type LocalBoxFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + 'a>>;
///
/// trait PropertyFetcher {
/// fn fetch(
/// &self,
/// key: &str,
/// ) -> LocalBoxFuture<Option<String>>;
/// }
///
/// struct WeakFetcher {
/// sub_obj: Weak<Box<PropertyFetcher>>,
/// }
///
/// impl PropertyFetcher for WeakFetcher {
/// fn fetch(&self, key: &str) -> LocalBoxFuture<Option<String>> {
/// if let Some(arc) = self.sub_obj.upgrade() {
/// // error[E0515]: cannot return value referencing local variable `arc`
/// arc.fetch(key).boxed_local()
/// } else {
/// ready(None).boxed_local()
/// }
/// }
/// }
/// ```
///
/// Thinking about it, this makes perfect sense: since `sub_obj` is a weak reference, it could
/// be dropped at any moment, violating the lifetime guarantee for the return value of `fetch()`.
/// To fix this, we need to ensure that the value we return internally keeps an `Arc` reference
/// to the object that created it. That's where `ArcGuard` comes in:
///
/// ```
/// # use futures::{future::ready,prelude::*};
/// # use std::sync::{Arc,Weak};
/// # use splot_core::{ArcGuard,ArcGuardExt};
/// #
/// # // Note: This next line is needed because `LocalBoxFuture` isn't in the `futures` crate yet.
/// # pub type LocalBoxFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + 'a>>;
/// #
/// # trait PropertyFetcher {
/// # fn fetch(
/// # &self,
/// # key: &str,
/// # ) -> LocalBoxFuture<Option<String>>;
/// # }
/// #
/// # struct WeakFetcher {
/// # sub_obj: Weak<Box<PropertyFetcher>>,
/// # }
///
/// impl PropertyFetcher for WeakFetcher {
/// fn fetch(&self, key: &str) -> LocalBoxFuture<Option<String>> {
/// if let Some(arc) = self.sub_obj.upgrade() {
/// // Compiles and works!
/// arc.guard(|x|x.fetch(key)).boxed_local()
/// } else {
/// ready(None).boxed_local()
/// }
/// }
/// }
/// ```
#[derive(Debug, Clone)]
pub struct ArcGuard<RC, T> {
inner: T,
head: Arc<RC>,
}
impl<RC, T> ArcGuard<RC, T> {
unsafe_pinned!(inner: T);
/// Constructs a new `ArcGuard<>` instance using the given `Arc<>` and getter closure.
/// The use of a closure for the getter allows for a more convenient syntax while ensuring
/// the lifetimes are properly accounted for.
///
/// See the main documentation for `ArcGuard<>` for a usage example.
pub fn new<'head, F>(head: Arc<RC>, getter: F) -> ArcGuard<RC, T>
where
F: FnOnce(&'head RC) -> T,
RC: 'head,
T: 'head,
{
// SAFETY: This is safe because we are only using this reference to create our object,
// and, by holding a reference to `head`, this class ensures that it does not live longer
// than the contained reference.
ArcGuard {
inner: getter(unsafe { std::mem::transmute::<&RC, &RC>(&head) }),
head,
}
}
pub fn parent(&self) -> Arc<RC> {
self.head.clone()
}
}
impl<RC, T> Deref for ArcGuard<RC, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<RC, T: std::fmt::Display> std::fmt::Display for ArcGuard<RC, T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
self.inner.fmt(f)
}
}
impl<RC, T> AsRef<T> for ArcGuard<RC, T> {
fn as_ref(&self) -> &T {
&self.inner
}
}
impl<RC, T> std::borrow::Borrow<T> for ArcGuard<RC, T> {
fn borrow(&self) -> &T {
&self.inner
}
}
impl<RC, T: Future> Future for ArcGuard<RC, T> {
type Output = T::Output;
fn poll(
mut self: Pin<&mut Self>,
cx: &mut futures::task::Context,
) -> futures::task::Poll<Self::Output> {
self.as_mut().inner().poll(cx)
}
}
impl<RC, T: Stream> Stream for ArcGuard<RC, T> {
type Item = T::Item;
fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut futures::task::Context,
) -> futures::task::Poll<Option<Self::Item>> {
self.as_mut().inner().poll_next(cx)
}
}
/// A convenience trait for `Arc<>` that makes it easier to construct `ArcGuard<>` instances.
pub trait ArcGuardExt<RC> {
fn guard<'head, F, T>(&self, getter: F) -> ArcGuard<RC, T>
where
F: FnOnce(&'head RC) -> T,
RC: 'head,
T: 'head;
}
impl<RC> ArcGuardExt<RC> for Arc<RC> {
fn guard<'head, F, T>(&self, getter: F) -> ArcGuard<RC, T>
where
F: FnOnce(&'head RC) -> T,
RC: 'head,
T: 'head,
{
ArcGuard::new(self.clone(), getter)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment