-
-
Save darconeous/a37f2569117eb7e81357a3f62d841528 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains hidden or 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
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