在函数中使用不可单态化的泛型参数

Using non-monomorphizable generic parameters in a function

我想编写一些代码,这些代码在特定特征的实现者之间应该是通用的,但直到运行时才能知道其确切类型。

特别是,我想将 PartialOrd 中的函数应用于给定枚举中的部分案例。这是我要实现的基本版本:

enum Value {
    String(String),
    Float(f64),
    Array(Vec<Value>),
}

fn apply_op<T, F>(v: &Value, compare_val: &Value, op: F) -> bool
where
    F: FnOnce(&T, &T) -> bool,
    T: PartialOrd,
{
    match (v, compare_val) {
        (Value::String(s), Value::String(compare_str)) => op(s, compare_str),
        (Value::Float(f), Value::Float(compare_f)) => op(f, compare_f),
        _ => false,
    }
}

然后为 op 传入类似 PartialOrd::gt 的内容。

由于类型 T 由 match arm 确定,因此无法单态化,因此无法编译。

有没有办法解决这个问题,也许可以使用某种 trait/wrapper 结构骗局?

Playground link here,其中包括上面的版本(无法编译)和我目前用来解决这个问题的宏方法。宏没问题,不过真的感觉不用宏也可以。

您不能 late-bind 通用函数,但您可以为此创建特征:

pub trait Comparator<T> {
    fn compare(self, a: &T, b: &T) -> bool;
}

然后为每个比较器创建一个类型,例如Gt:

struct Gt;
// You can also implement it individually for `String`, `f64` and `Vec<Value>`.
impl<T: PartialOrd> Comparator<T> for Gt {
    fn compare(self, a: &T, b: &T) -> bool {
        PartialOrd::gt(a, b)
    }
}

然后:

fn apply_op<F>(v: &Value, compare_val: &Value, op: F) -> bool
where
    F: Comparator<String> + Comparator<f64> + Comparator<Vec<Value>>,
{
    match (v, compare_val) {
        (Value::String(s), Value::String(compare_str)) => op.compare(s, compare_str),
        (Value::Float(f), Value::Float(compare_f)) => op.compare(f, compare_f),
        _ => false,
    }
}