如何使用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(¶ms).unwrap();
func1.apply(¶ms).unwrap();
func2.apply(¶ms).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(), ¶ms);
container.call(&"print_str".to_string(), ¶ms);
}
上面的实现还有一些缺点,函数的输入是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(), ¶ms);
}