pub trait Isa<T: ?Sized> {
fn isa(&self) -> bool;
}
pub trait Cast<T> {
fn cast(&self) -> &T;
fn cast_mut(&mut self) -> &mut T;
}
pub trait DynCast<T> {
fn dyn_cast(&self) -> Option<&T>;
fn dyn_cast_mut(&mut self) -> Option<&mut T>;
}
pub fn cast_val<T, U>(val: T, f: impl FnOnce(T) -> Option<U>) -> U {
f(val).expect("cast<> failed: invalid type conversion")
}
pub fn dyn_cast_val<T, U>(val: T, f: impl FnOnce(T) -> Option<U>) -> Option<U> {
f(val)
}
pub fn isa_val<T>(val: &T, f: impl FnOnce(&T) -> bool) -> bool {
f(val)
}
pub trait TypeTag: PartialEq + Copy {
fn all() -> &'static [Self];
}
pub struct Tagged<T: TypeTag, V> {
pub tag: T,
pub value: V,
}
impl<T: TypeTag + std::fmt::Debug, V> Tagged<T, V> {
pub fn is_a(&self, tag: T) -> bool {
self.tag == tag
}
pub fn as_a(&self, tag: T) -> Option<&V> {
if self.tag == tag {
Some(&self.value)
} else {
None
}
}
pub fn cast_a(&self, tag: T) -> &V {
assert_eq!(
self.tag, tag,
"cast_a<> failed: expected {:?}, got {:?}",
tag, self.tag
);
&self.value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum MyTag {
Int,
Float,
String,
}
impl TypeTag for MyTag {
fn all() -> &'static [Self] {
&[MyTag::Int, MyTag::Float, MyTag::String]
}
}
#[test]
fn test_tagged_is_a() {
let v = Tagged {
tag: MyTag::Int,
value: 42i32,
};
assert!(v.is_a(MyTag::Int));
assert!(!v.is_a(MyTag::Float));
}
#[test]
fn test_tagged_as_a() {
let v = Tagged {
tag: MyTag::String,
value: "hello".to_string(),
};
assert!(v.as_a(MyTag::String).is_some());
assert!(v.as_a(MyTag::Int).is_none());
}
#[test]
fn test_cast_val_success() {
let result = cast_val(Some(42), |v| v);
assert_eq!(result, 42);
}
#[test]
#[should_panic]
fn test_cast_val_failure() {
let _: i32 = cast_val(None::<i32>, |v| v);
}
}