Skip to content

Commit

Permalink
feat(hydroflow): add StateHandleErased (no type param) for storage …
Browse files Browse the repository at this point in the history
…in data structures, fix #1048
  • Loading branch information
discord9 authored and MingweiSamuel committed Feb 2, 2024
1 parent 87e86a2 commit 0c657ae
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 2 deletions.
2 changes: 1 addition & 1 deletion hydroflow/src/scheduled/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ impl Display for HandoffId {

/// A staten handle's ID. Invalid if used in a different [`graph::Hydroflow`]
/// instance than the original that created it.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct StateId(pub(crate) usize);
74 changes: 73 additions & 1 deletion hydroflow/src/scheduled/state.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
//! Module for [`StateHandle`], part of the "state API".
use std::any::{Any, TypeId};
use std::marker::PhantomData;

use super::StateId;

/// A handle into a particular [`Hydroflow`](super::graph::Hydroflow) instance, referring to data
/// inserted by [`add_state`](super::graph::Hydroflow::add_state).
///
/// If you need to store state handles in a data structure see [`StateHandleErased`] which hides
/// the generic type parameter.
#[must_use]
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StateHandle<T> {
pub(crate) state_id: StateId,
pub(crate) _phantom: PhantomData<*mut T>,
Expand All @@ -18,3 +22,71 @@ impl<T> Clone for StateHandle<T> {
*self
}
}

/// A state handle with the generic type parameter erased, allowing it to be stored in omogenous
/// data structures. The type is tracked internally as data via [`TypeId`].
///
/// Use [`StateHandleErased::from(state_handle)`](StateHandleErased::from) to create an instance
/// from a typed [`StateHandle<T>`].
///
/// Use [`StateHandle::<T>::try_from()`](StateHandle::try_from) to convert the `StateHandleErased`
/// back into a `StateHandle<T>` of the given type `T`. If `T` is the wrong type then the original
/// `StateHandleErased` will be returned as the `Err`.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StateHandleErased {
state_id: StateId,
type_id: TypeId,
}

/// See [`StateHandleErased`].
impl<T> TryFrom<StateHandleErased> for StateHandle<T>
where
T: Any,
{
type Error = StateHandleErased;

fn try_from(value: StateHandleErased) -> Result<Self, Self::Error> {
if TypeId::of::<T>() == value.type_id {
Ok(Self {
state_id: value.state_id,
_phantom: PhantomData,
})
} else {
Err(value)
}
}
}
/// See [`StateHandleErased`].
impl<T> From<StateHandle<T>> for StateHandleErased
where
T: Any,
{
fn from(value: StateHandle<T>) -> Self {
Self {
state_id: value.state_id,
type_id: TypeId::of::<T>(),
}
}
}

#[cfg(test)]
mod test {
use std::marker::PhantomData;

use super::*;
use crate::scheduled::StateId;

#[test]
fn test_erasure() {
let handle = StateHandle::<String> {
state_id: StateId(0),
_phantom: PhantomData,
};
let handle_erased = StateHandleErased::from(handle);
let handle_good = StateHandle::<String>::try_from(handle_erased);
let handle_bad = StateHandle::<&'static str>::try_from(handle_erased);

assert_eq!(Ok(handle), handle_good);
assert_eq!(Err(handle_erased), handle_bad);
}
}

0 comments on commit 0c657ae

Please sign in to comment.