Rust特征与泛型的灵活用法

如何使用Rust实现以下类似的功能

下面代码来自axum(Rust的web框架)

async fn json(Json(payload): Json<Value>) {
//TODO
}
async fn query(Query(params): Query<HashMap<String, String>>) {
//TODO
}
Router::new()
       .route("/query", post(query))
       .route("/json", post(json))

其中json(Json(payload): Json<Value>) 是利用Rust的模式匹配进行解构,但是这里最让人迷惑的是Router的route方法。因为route函数可以接受不同签名的函数,但是在Rust是强类型语言,不同的函数签名是被视为不同类型的函数。下面我们来实现这种模式。

我们希望对于不同函数都可以调用同一个方法,并且接受同一种类型参数,

	let params = Params {
        vals: vec![Value::new(1), Value::new(2), Value::new(3)],
    };
    let func = || {
        println!("empty");
    };

    let func1 = |msg: String| {
        println!("{msg}");
    };
    let func2 = |msg: String, num: i64| {
        println!("{msg} {num}");
    };
    func.apply(&params).unwrap();
    func1.apply(&params).unwrap();
    func2.apply(&params).unwrap();

由于这种函数都有同一种类型的方法,于是我们可以定义一个trait,然后为不同类型函数实现这个trait

trait Handle<T = ()> {
    fn apply(&self, params: &Params) -> Result<(), String>;
}

Params由各种Value组成,我们需要将Params里面的Value解析为函数的参数,这个是实现apply方法可以接受不同签名的函数的关键,因此这里也需要定义一个trait

#[derive(Clone, Copy)]
struct Value {
    inner: i64, //这里1表示String,2表示Number,3表示Boolean,
}
impl Value {
    fn new(inner: i64) -> Self {
        Self { inner }
    }
}
struct Params {
    vals: Vec<Value>,
}
//定义一个转换特征
trait FromValue: Sized {
    fn from(value: Value) -> Result<Self, String>;
}
//为String、i64、bool类型实现这个特征,代表Value可以转换成为这3种类型。
impl FromValue for String {
    fn from(value: Value) -> Result<Self, String> {
        if value.inner != 1 {
            return Err(format!("The string cannot come from {}", value.inner));
        }
        Ok("js_value".to_string())
    }
}

impl FromValue for i64 {
    fn from(value: Value) -> Result<Self, String> {
        if value.inner != 2 {
            return Err(format!("The number cannot come from {}", value.inner));
        }
        Ok(123456)
    }
}

impl FromValue for bool {
    fn from(value: Value) -> Result<Self, String> {
        if value.inner != 3 {
            return Err(format!("The boolean cannot come from {} ,", value.inner));
        }
        Ok(true)
    }
}

接下来我们就需要为不同签名的函数实现Handle特征。这里都是些重复代码,可以用宏来实现。

impl<F, T> Handle<T> for F
where
    F: Fn(T),
    T: FromValue,
{
    fn apply(&self, params: &Params) -> Result<(), String> {
        let t = T::from(params.vals[0])?;
        (self)(t);
        Ok(())
    }
}
//注意Handle只有一个泛型T,但是对于有两个参数的函数需要声明两个不同的泛型,
//在Rust中impl<F, T1, T2> 这里的泛型表示声明,Handle<(T1, T2)> 这里的泛型表示使用,
//impl语句中可以声明新的泛型参数R,但是R必须受到约束,也就是R必须impl语句块中泛型参数有关联,后面会有具体的用法
impl<F, T1, T2> Handle<(T1, T2)> for F
where
    F: Fn(T1, T2),
    T1: FromValue,
    T2: FromValue,
{
    fn apply(&self, params: &Params) -> Result<(), String> {
        (self)(T1::from(params.vals[0])?, T2::from(params.vals[1])?);
        Ok(())
    }
}
impl<F, T1, T2, T3> Handle<(T1, T2, T3)> for F
where
    F: Fn(T1, T2, T3),
    T1: FromValue,
    T2: FromValue,
    T3: FromValue,
{
    fn apply(&self, params: &Params) -> Result<(), String> {
        (self)(
            T1::from(params.vals[0])?,
            T2::from(params.vals[1])?,
            T3::from(params.vals[2])?,
        );
        Ok(())
    }
}

这样不同的函数都可以调用同一个方法。
接下来我们将这些实现了Handle特征的函数收集到一个容器里面,然后按需调用。
容器里面只能存放一种类型,所以是不能直接存放不同签名的函数,因为它们是是不同的类型,
但是这些函数都实现了Handle特征,所以我们可以存放特征对象,并且该特征对象是安全的。

struct BoxedHandle<T>(Box<dyn Handle<T>>);

impl<T> Handle for BoxedHandle<T> {
    fn apply(&self, params: &Params) -> Result<(), String> {
        self.0.apply(params)
    }
}

impl<T> BoxedHandle<T> {
    fn new<F>(f: F) -> Self
    where
        F: Handle<T> + 'static,
    {
        Self(Box::new(f))
    }
}

impl Container {
    fn new() -> Self {
        Self {
            map: HashMap::new(),
        }
    }
    fn add<T, F>(&mut self, name: String, f: F)
    where
        T: 'static,
        F: Handle<T> + 'static,
    {
        self.map.insert(name, Box::new(BoxedHandle::new(f)));
    }
    fn call(&self, name: &String, params: &Params) {
        self.map
            .get(name)
            .map(|handle| handle.apply(params).unwrap());
    }
}

然后我们就可以从容器中调用任意函数。

fn test_container() {
    let mut container = Container::new();
    container.add("empty".to_string(), || println!("empty"));
    container.add("print_str".to_string(), |s: String| println!("s = {s}"));
    container.add("print_str_bool".to_string(), |s: String, b: bool| {
        println!("s = {s}, b = {b}")
    });
    let params = Params {
        vals: vec![Value::new(1), Value::new(2), Value::new(3)],
    };
    
    container.call(&"empty".to_string(), &params);
    container.call(&"print_str".to_string(), &params);
}

上面的实现还有一些缺点,函数的输入是Params,函数的返回(),这样不够灵活,我们希望Container可以输入其他类型的Params,可以添加不同签名的函数,接下来我们将上面的实现全部替换成泛型与特征。

trait FromValue: Sized {
    type Input;
    fn from(value: &Self::Input) -> Result<Self, String>;
}

trait IntoValue: Sized {
    type Output;
    fn into(self) -> Result<Self::Output, String>;
}

trait Handle<T = ()> {
    type Input;
    type Output;
    fn apply(&self, input: &Self::Input) -> Result<Self::Output, String>;
}
//这里对于无参函数的实现有一些问题
//因为Input指定了为(),导致后面类型不兼容
impl<R, F> Handle for F
where
    F: Fn() -> R,
    R: IntoValue,
{
    type Input = ();
    type Output = R::Output;
    fn apply(&self, _: &Self::Input) -> Result<Self::Output, String> {
        let r = (self)();
        Ok(r.into()?)
    }
}

impl<R, T, F> Handle<T> for F
where
    F: Fn(T) -> R,
    T: FromValue,
    R: IntoValue,
{
    type Input = T::Input;
    type Output = R::Output;
    fn apply(&self, input: &Self::Input) -> Result<Self::Output, String> {
        let r = (self)(T::from(input)?);
        Ok(r.into()?)
    }
}

impl<R, I, F, T1, T2> Handle<(T1, T2)> for F
where
    F: Fn(T1, T2) -> R,
    T1: FromValue<Input = I>,
    T2: FromValue<Input = I>,
    R: IntoValue,
{
    type Input = I;
    type Output = R::Output;
    fn apply(&self, input: &Self::Input) -> Result<Self::Output, String> {
        let r = (self)(T1::from(input)?, T2::from(input)?);
        Ok(r.into()?)
    }
}

struct BoxedHandle<I, O, T>(Box<dyn Handle<T, Input = I, Output = O>>);

impl<I, O, T> Handle for BoxedHandle<I, O, T> {
    type Input = I;
    type Output = O;
    fn apply(&self, input: &Self::Input) -> Result<Self::Output, String> {
        self.0.apply(input)
    }
}

impl<I, O, T> BoxedHandle<I, O, T> {
    fn new<F>(f: F) -> Self
    where
        F: Handle<T, Input = I, Output = O> + 'static,
    {
        Self(Box::new(f))
    }
}

struct Container<K, I, O> {
    map: HashMap<K, Box<dyn Handle<Input = I, Output = O>>>,
}
impl<K: Ord + Hash, I, O: std::fmt::Debug> Container<K, I, O> {
    fn new() -> Self {
        Self {
            map: HashMap::new(),
        }
    }
    fn add<T, F>(&mut self, key: K, f: F)
    where
        I: 'static,
        O: 'static,
        T: 'static,
        F: Handle<T, Input = I, Output = O> + 'static,
    {
        self.map.insert(key, Box::new((BoxedHandle::new(f))));
    }
    fn call(&self, key: &K, input: &I) -> Option<Result<O, String>> {
        self.map.get(key).map(|h| h.apply(input))
    }
}

然后只需要为String、bool、i64实现FromValue、IntoValue特征就可以了。

#[derive(Clone, Copy, Debug)]
struct Value {
    inner: i64, //1:str,2:num,3:bool,
}
impl Value {
    fn new(inner: i64) -> Self {
        Self { inner }
    }
}

#[derive(Clone, Debug)]
struct Params {
    vals: Vec<Value>,
}

impl FromValue for String {
    type Input = Params;
    fn from(value: &Self::Input) -> Result<Self, String> {
        for v in value.vals.iter() {
            if v.inner == 1 {
                return Ok("Value = String".to_string());
            }
        }
        Err(format!("The string cannot come from  {:?}", value))
    }
}

impl FromValue for i64 {
    type Input = Params;
    fn from(value: &Self::Input) -> Result<Self, String> {
        for v in value.vals.iter() {
            if v.inner == 2 {
                return Ok(2);
            }
        }
        Err(format!("The number cannot come from {:?}", value))
    }
}

impl FromValue for bool {
    type Input = Params;
    fn from(value: &Self::Input) -> Result<Self, String> {
        for v in value.vals.iter() {
            if v.inner == 3 {
                return Ok(true);
            }
        }
        Err(format!("The boolean cannot come from {:?}", value))
    }
}

impl IntoValue for () {
    type Output = Value;
    fn into(self) -> Result<Self::Output, String> {
        Ok(Value::new(0))
    }
}

impl IntoValue for String {
    type Output = Value;
    fn into(self) -> Result<Self::Output, String> {
        Ok(Value::new(1))
    }
}

impl IntoValue for i64 {
    type Output = Value;
    fn into(self) -> Result<Self::Output, String> {
        Ok(Value::new(2))
    }
}

impl IntoValue for bool {
    type Output = Value;
    fn into(self) -> Result<Self::Output, String> {
        Ok(Value::new(3))
    }
}

测试

fn test_container(){
	let mut container = Container::new();
    //container.add("empty".to_string(),|| println!("empty"));
    container.add("print_str_num".to_string(), |s: String, n: i64| s);
    container.add("print_num".to_string(), |n: i64| n);

    let h = "hello".to_string();
    container.add("print_bool_str".to_string(), move |b: bool, s: String| {
        format!("{h}, {s}")
    });
    let params = Params {
        vals: vec![Value::new(1), Value::new(2), Value::new(3)],
    };

    container.call(&"print_boo_str".to_string(), &params);
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值