嵌套特征类型、有界类型和使用 as 关键字时的冗长

Nested trait types, bounded types and verbosity in use of the as keyword

我对 as 关键字在 traits 中嵌套类型的可读性和冗长性有疑问。

我有 C++ 背景,我怀疑我的误解是由于对使用的类型系统缺乏了解 Haskell,我知道 Rust 会使用它。

在 C++ 中,模板化结构用作特征,其中类型是根据模板参数定义的。嵌套可以发生在结构等中定义其他结构类型的地方

Rust 似乎支持类似的功能。 struct MyStruct<F: Foo> 是下面较大代码片段的一部分,它根据提供的类型 F 中的类型定义成员,绑定到特征 Foo:

struct MyStruct<F: Foo>  {
// Why does this not work?
// Surely F, being constrainted to a Foo,
//   has a type BAR, constrained to Bar<F>,
//   has a type BAZ, constrained to Baz<F>
//        data: Vec<F::BAR::BAZ>,
    data: Vec< <<F as Foo>::BAR as Bar<F>>::BAZ >,
}

简而言之,当在此结构中定义成员时,似乎必须使用 <<F as Foo>::BAR as Bar<F>>::BAZ 来为编译器提供额外的类型信息。这似乎是合理的,因为编译器必须知道 F 的类型才能推断其类型。然而,在我看来,这些信息已经由这些类型的边界、结构的通用参数以及特征定义本身提供。

我确实发现 <<F as Foo>::BAR as Bar<F>>::BAZF::BAR::BAZ 相比有点难读,我想知道从代码可读性的角度来看是否有更好的处理方法?完整代码片段如下:

use std::vec::{Vec};

pub trait Foo {
    type VALUE;
    type BAR: Bar<Self>;
}

pub trait Bar<F: Foo> {
    type BAZ: Baz<F>;    
}

pub trait Baz<F: Foo> {
    fn new(value: F::VALUE) -> Box<F::VALUE> {
        Box::new(value)
    }
}

fn main() {
    struct BarImpl;

    impl<F: Foo> Bar<F> for BarImpl {
        type BAZ = BazImpl;
    }

    struct FooImpl;

    impl Foo for FooImpl {
        type VALUE = f64;
        type BAR = BarImpl;
    }

    struct BazImpl { dummy: i32 };

    impl<F: Foo> Baz<F> for BazImpl {};


    struct MyStruct<F: Foo>  {
    // Why does this not work?
    // Surely F, being constrainted to a Foo,
    //   has a type BAR, constrained to Bar<F>,
    //   has a type BAZ, constrained to Baz<F>
    //        data: Vec<F::BAR::BAZ>,
        data: Vec< <<F as Foo>::BAR as Bar<F>>::BAZ >,
    }

    let mut s = MyStruct::<FooImpl> { data: Vec::new() };

    for x in 0..5 {
        let b = BazImpl{ dummy: x};
        s.data.push(b);
    }

    println!("s.data.len() = {}", s.data.len());
}

第一关可以隐含,所以可以有Vec<<F::BAR as Bar<F>>::BAZ>。很有可能以后规则会放宽,可以应对多层次推断合适的约束条件,但现在不是这样。

您可以创建一个type alias

type FooBarBaz<F> = <<F as Foo>::BAR as Bar<F>>::BAZ;

struct MyStruct<F: Foo>  {
    data: Vec< FooBarBaz<F> >,
}