typed_eval/
dyn_fn.rs

1use crate::{Error, EvalType, Result, TypeInfo};
2use std::{any::Any, ops::Deref};
3
4// the function with a dynamically known type
5pub struct DynFn {
6    pub arg_type: TypeInfo,
7    pub ret_type: TypeInfo,
8    boxed_fun: Box<dyn ClonableAny>,
9}
10
11impl DynFn {
12    pub fn new<Arg, Ret>(
13        f: impl for<'a> Fn(&'a Arg) -> Ret::RefType<'a> + Clone + 'static,
14    ) -> Self
15    where
16        Arg: EvalType,
17        Ret: EvalType,
18    {
19        Self {
20            boxed_fun: Box::new(BoxedFn(Box::new(f))),
21            arg_type: Arg::type_info(),
22            ret_type: Ret::type_info(),
23        }
24    }
25
26    pub fn downcast<Arg, Ret>(&self) -> Result<BoxedFn<Arg, Ret>>
27    where
28        Arg: EvalType,
29        Ret: EvalType,
30    {
31        Ok(self.boxed_fun.as_any().downcast_ref().cloned().ok_or_else(
32            || Error::InternalDynFnDowncastError {
33                expected_arg: Arg::type_info(),
34                expected_ret: Ret::type_info(),
35                got_arg: self.arg_type,
36                got_ret: self.ret_type,
37            },
38        )?)
39    }
40}
41
42impl Clone for DynFn {
43    fn clone(&self) -> Self {
44        DynFn {
45            boxed_fun: self.boxed_fun.clone_box(),
46            ..*self
47        }
48    }
49}
50
51// the function with a statically known type
52pub struct BoxedFn<Arg, Ret>(Box<dyn ClonableFn<Arg, Ret>>)
53where
54    Ret: EvalType;
55
56impl<Arg, Ret> Clone for BoxedFn<Arg, Ret>
57where
58    Ret: EvalType,
59{
60    fn clone(&self) -> Self {
61        self.clone_boxed()
62    }
63}
64
65// implementing Deref allows to use the call syntax on the BoxedFn instances
66impl<Arg, Ret> Deref for BoxedFn<Arg, Ret>
67where
68    Ret: EvalType,
69{
70    type Target = Box<dyn ClonableFn<Arg, Ret>>;
71    fn deref(&self) -> &Self::Target {
72        &self.0
73    }
74}
75
76trait ClonableAny: Any {
77    fn clone_box(&self) -> Box<dyn ClonableAny>;
78    fn as_any(&self) -> &dyn Any;
79}
80
81impl<T: Any + Clone> ClonableAny for T {
82    fn clone_box(&self) -> Box<dyn ClonableAny> {
83        Box::new(self.clone())
84    }
85    fn as_any(&self) -> &dyn Any {
86        self
87    }
88}
89
90pub trait ClonableFn<Arg, Ret>:
91    for<'a> Fn(&'a Arg) -> Ret::RefType<'a>
92where
93    Ret: EvalType,
94{
95    fn clone_boxed(&self) -> BoxedFn<Arg, Ret>;
96}
97
98// implement ClonableFn for every matching function
99impl<Arg, Ret, F> ClonableFn<Arg, Ret> for F
100where
101    Ret: EvalType,
102    F: for<'a> Fn(&'a Arg) -> Ret::RefType<'a> + Clone + 'static,
103{
104    fn clone_boxed(&self) -> BoxedFn<Arg, Ret> {
105        BoxedFn(Box::new(self.clone()))
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn test_dyn_fn() {
115        // here we construct a function that takes an (i64,i64) tuple and returns the first part
116        // but the type of the variable just DynFn, no mention of tuples and i64
117        let dyn_fn = DynFn::new::<_, i64>(|a: &(i64, i64)| a.0);
118
119        // here we get back to the callable function with known types
120        // but for that we need to know exact types at compile time
121        let concrete_fn = dyn_fn.downcast::<(i64, i64), i64>().unwrap();
122
123        // and here we call the downcasted function to test if it really works as intended
124        assert_eq!((concrete_fn)(&(10, 20)), 10);
125    }
126}