From 89b17c409d5bc804dd0f2c2e08232be4bbeefdb9 Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Sat, 6 Dec 2025 23:26:02 +0100 Subject: [PATCH] Replace `dyn Any` with `TulispAny` trait requiring Display --- src/builtin/functions/hash_table.rs | 30 ++++++++++++++++++----------- src/lib.rs | 1 + src/object.rs | 14 ++++++++++---- src/value.rs | 18 ++++++++++++----- tests/tests.rs | 12 +++++++++--- 5 files changed, 52 insertions(+), 23 deletions(-) diff --git a/src/builtin/functions/hash_table.rs b/src/builtin/functions/hash_table.rs index 89e5b33..5cefb7c 100644 --- a/src/builtin/functions/hash_table.rs +++ b/src/builtin/functions/hash_table.rs @@ -1,5 +1,5 @@ -use crate::{Error, ErrorKind, TulispContext, TulispObject, destruct_eval_bind}; -use std::{any::Any, cell::RefCell, collections::HashMap, rc::Rc}; +use crate::{Error, ErrorKind, TulispAny, TulispContext, TulispObject, destruct_eval_bind}; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; struct TulispObjectEql(TulispObject); @@ -28,6 +28,16 @@ impl From for TulispObjectEql { } } +pub(crate) struct HashTable { + inner: RefCell>, +} + +impl std::fmt::Display for HashTable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#") + } +} + pub(crate) fn add(ctx: &mut TulispContext) { ctx.add_special_form("make-hash-table", |_ctx, args| { if !args.null() { @@ -37,18 +47,18 @@ pub(crate) fn add(ctx: &mut TulispContext) { ) .with_trace(args.clone())); } - let table: Rc = - Rc::new(RefCell::new(HashMap::::new())); + let table: Rc = Rc::new(HashTable { + inner: RefCell::new(HashMap::new()), + }); Ok(table.into()) }); ctx.add_special_form("gethash", |ctx, args| { destruct_eval_bind!(ctx, (key table) = args); let binding = table.as_any()?; - let table = binding - .downcast_ref::>>() - .unwrap(); + let table = binding.downcast_ref::().unwrap(); let value = table + .inner .borrow_mut() .get(&key.into()) .cloned() @@ -61,10 +71,8 @@ pub(crate) fn add(ctx: &mut TulispContext) { destruct_eval_bind!(ctx, (key value table) = args); let binding = table.as_any()?; - let table = binding - .downcast_ref::>>() - .unwrap(); - table.borrow_mut().insert(key.into(), value); + let table = binding.downcast_ref::().unwrap(); + table.inner.borrow_mut().insert(key.into(), value); Ok(TulispObject::nil()) }); diff --git a/src/lib.rs b/src/lib.rs index 9fedf09..c5ce973 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,6 +94,7 @@ pub use error::{Error, ErrorKind}; pub mod lists; mod value; +pub use value::TulispAny; #[doc(hidden)] pub use value::TulispValue; diff --git a/src/object.rs b/src/object.rs index 6bbc467..8d4609f 100644 --- a/src/object.rs +++ b/src/object.rs @@ -2,6 +2,7 @@ use crate::{ ErrorKind, TulispValue, cons::{self, Cons}, error::Error, + value::TulispAny, }; use std::{ any::Any, @@ -289,8 +290,7 @@ with `as_any`, and downcast to desired types. ## Example ```rust -# use tulisp::{TulispContext, destruct_bind, Error}; -# use std::any::Any; +# use tulisp::{TulispContext, destruct_bind, Error, TulispAny}; # use std::rc::Rc; # # fn main() -> Result<(), Error> { @@ -300,11 +300,17 @@ struct TestStruct { value: i64, } +impl std::fmt::Display for TestStruct { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "\"TestStruct {}\"", self.value) + } +} + ctx.add_special_form("make_any", |_ctx, args| { destruct_bind!((inp) = args); let inp: i64 = inp.try_into()?; - let any_obj: Rc = Rc::new(TestStruct { value: inp }); + let any_obj: Rc = Rc::new(TestStruct { value: inp }); Ok(any_obj.into()) }); @@ -569,7 +575,7 @@ tulisp_object_from!(f64); tulisp_object_from!(&str); tulisp_object_from!(String); tulisp_object_from!(bool); -tulisp_object_from!(Rc); +tulisp_object_from!(Rc); impl FromIterator for TulispObject { fn from_iter>(iter: T) -> Self { diff --git a/src/value.rs b/src/value.rs index 3f4d4b8..0b87923 100644 --- a/src/value.rs +++ b/src/value.rs @@ -5,7 +5,12 @@ use crate::{ error::{Error, ErrorKind}, object::Span, }; -use std::{any::Any, convert::TryInto, fmt::Write, rc::Rc}; +use std::{ + any::Any, + convert::TryInto, + fmt::{Display, Write}, + rc::Rc, +}; #[doc(hidden)] #[derive(Debug, Clone)] @@ -196,6 +201,9 @@ impl SymbolBindings { } } +pub trait TulispAny: Any + Display {} +impl TulispAny for T {} + #[doc(hidden)] #[derive(Clone)] pub enum TulispValue { @@ -237,7 +245,7 @@ pub enum TulispValue { Splice { value: TulispObject, }, - Any(Rc), + Any(Rc), Func(Rc), Macro(Rc), Defmacro { @@ -281,7 +289,7 @@ impl std::fmt::Debug for TulispValue { Self::Backquote { value } => f.debug_struct("Backquote").field("value", value).finish(), Self::Unquote { value } => f.debug_struct("Unquote").field("value", value).finish(), Self::Splice { value } => f.debug_struct("Splice").field("value", value).finish(), - Self::Any(arg0) => f.debug_tuple("Any").field(arg0).finish(), + Self::Any(arg0) => write!(f, "Any({:?} = {})", arg0.type_id(), arg0), Self::Func(_) => write!(f, "Func"), Self::Macro(_) => write!(f, "Macro"), Self::Defmacro { params, body } => f @@ -849,8 +857,8 @@ impl From for TulispValue { } } -impl From> for TulispValue { - fn from(value: Rc) -> Self { +impl From> for TulispValue { + fn from(value: Rc) -> Self { TulispValue::Any(value) } } diff --git a/tests/tests.rs b/tests/tests.rs index c278f81..4f21911 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,5 +1,5 @@ -use std::{any::Any, rc::Rc}; -use tulisp::{Error, Iter, TulispContext, TulispObject, destruct_eval_bind}; +use std::{fmt::Display, rc::Rc}; +use tulisp::{Error, Iter, TulispAny, TulispContext, TulispObject, destruct_eval_bind}; macro_rules! tulisp_assert { (@impl $ctx: expr, program:$input:expr, result:$result:expr $(,)?) => { @@ -1155,11 +1155,17 @@ fn test_any() -> Result<(), Error> { struct TestStruct { value: i64, } + impl Display for TestStruct { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "TestStruct({})", self.value) + } + } + let mut ctx = TulispContext::new(); ctx.add_special_form("make_any", |ctx, args| { destruct_eval_bind!(ctx, (inp) = args); - let res: Rc = Rc::new(TestStruct { + let res: Rc = Rc::new(TestStruct { value: inp.try_into()?, });