如何使用Rust枚举,结构,特征或其他任何东西来构造包含存在类型的值?

时间:2016-03-01 17:46:30

标签: rust

什么Rust构造大致完成与以下OCaml相同的事情?

type t = F : 'x * ('x -> string) -> t

let int_eg = F(1, string_of_int)
let str_eg = F("foo", fun x -> x)

let print x = print_string (match x with
  | F(x,to_str) -> to_str x)

1 个答案:

答案 0 :(得分:2)

你可以获得存在类型的最接近的东西是特质对象:

// how ToString is declared
trait ToString {
    fn to_string(&self) -> String;
}

let i32_str: Box<ToString> = Box::new(1);
let str_str: Box<ToString> = Box::new("foo");

fn print(value: &ToString) -> String {
    value.to_string()
}
print_x(&i32_str);  // automatically coerced from Box<ToString> to &ToString
print_x(&str_str);

对于特征对象,实际类型将被删除,唯一剩下的就是知道这个特定值是某种类型,它实现了给定的特征。它与Haskell中具有类类边界的存在类型非常相似:

data Showable = Showable (forall a. Show a => a)

无法将任意函数与任意类型捆绑在一起,从容器签名中删除它,因此您需要使用特征。幸运的是,traits很容易为任意类型定义和实现,因此您始终可以定义特征并使用特征对象。 Trait对象几乎涵盖了ML / Haskell中通常需要存在的所有功能。

此外,在许多情况下,您根本不需要使用特质对象!例如,上面的print()函数实际上应该写成如下:

fn print<T: ToString>(value: &T) -> String {
    value.to_string()
}

此类函数功能更强大,因为它适用于ToString特征的任意实现者,其中包括由ToString构成的特征对象,以及实现ToString的所有其他特征。通常使用特征对象的唯一地方是定义异构数据结构时:

let many_to_strings: Vec<Box<ToString>> = vec![Box::new(1), Box::new("foo")];

但是,正如我上面所说,当你消费特质对象时,在大多数情况下你不需要指定你需要一个特质对象 - 一个普通的泛型函数会更惯用。