泛型参数

语法
泛型参数组 :
      < >
   | < (泛型参数 ,)* 泛型参数 ,? >

泛型参数 :
   外围属性* ( 生命周期参数 | 类型参数 | 常量参数 )

生命周期参数 :
   生命周期或标签 ( : 生命周期约束组 )?

类型参数 :
   标识符( : 类型参数约束组? )? ( = 类型 )?

常量参数:
   const 标识符 : 类型 ( = | 标识符 | -?字面值 )?

函数类型别名结构体枚举联合体traits实现 可以通过类型、常量和生命周期参数 泛型化 。 这些参数在尖括号 (<...>) 中列出,通常紧跟在条目名称之后,在其定义之前。 对于没有名称的实现,直接在 impl 之后。泛型参数的顺序仅限于首先为生命周期参数,然后交替出现类型和常量参数。

一些带有类型、常量和生命周期参数的条目的例子:

#![allow(unused)]
fn main() {
fn foo<'a, T>() {}
trait A<U> {}
struct Ref<'a, T> where T: 'a { r: &'a T }
struct InnerArray<T, const N: usize>([T; N]);
struct EitherOrderWorks<const N: bool, U>(U);
}

泛型参数在其声明的条目定义中是有效的。根据 条目声明 所述,不在函数体中声明的条目作用域内。

引用原始指针数组切片元组 , 和 函数指针 也有生命周期或类型参数,但不是用路径语法来引用。

常量泛型

常量泛型参数 指条目泛型为常量值。 const 标识符引入了常量参数的名称,并且必须使用给定类型的值来实例化条目实例。

仅允许的常量参数类型有 u8u16u32u64u128usizei8i16i32i64i128isizecharbool.

常量参数可以在任何 常量条目 可用的地方使用,但是当常量参数用于 类型数组重复表达式 时,必须是独立的 (如下所述) 。也就是说,允许出现在以下位置:

  1. 作为相关条目签名一部分的类型应用 const 。
  2. 作为定义 关联常量 或作为 关联类型 参数的常量表达式的一部分。
  3. 作为条目中任何函数体中运行时表达式中的值。
  4. 作为条目中任何函数体中使用的任何类型的参数。
  5. 作为条目字段类型的一部分。
#![allow(unused)]
fn main() {
// 可以使用常量泛型参数的例子。

// 用于条目本身签名。
fn foo<const N: usize>(arr: [i32; N]) {
    // 用作函数体中的类型。
    let x: [i32; N];
    // 用作表达式。
    println!("{}", N * 2);
}

// 用作结构体的字段。
struct Foo<const N: usize>([i32; N]);

impl<const N: usize> Foo<N> {
    // 用作关联常量。
    const CONST: usize = N * 4;
}

trait Trait {
    type Output;
}

impl<const N: usize> Trait for Foo<N> {
    // 用作关联类型。
    type Output = [i32; N];
}
}
#![allow(unused)]
fn main() {
// 不能使用常量泛型参数的例子。
fn foo<const N: usize>() {
    // 不能在函数体中的条目定义上使用。
    const BAD_CONST: [usize; N] = [1; N];
    static BAD_STATIC: [usize; N] = [1; N];
    fn inner(bad_arg: [usize; N]) {
        let bad_value = N * 2;
    }
    type BadAlias = [usize; N];
    struct BadStruct([usize; N]);
}
}

作为进一步的限制,常量参数只能出现在 类型数组重复表达式 内部作为独立的参数。 在这些上下文中,它们只能用作单个段 路径表达式 ,可能包含在 中 (如 N{N} ) 。也就是说,它们不能与其他表达式结合使用。

#![allow(unused)]
fn main() {
// 不可以使用常量参数的例子。

// 不允许在其他类型的表达式中组合,比如这里的返回类型中的算术表达式。
fn bad_function<const N: usize>() -> [u8; {N + 1}] {
    // 同样不允许用于数组重复表达式。
    [1; {N + 1}]
}
}

路径 中的常量参数指定用于该条目的常量值。 参数必须是为常量参数指定的类型的 常量表达式 。 除非它是单个路径段 (标识符) 或 字面值 (可能以 - 令牌开头) ,否则常量表达式必须是 块表达式 (用大括号括起来) 。

注意: 这种句法限制是必要的,以避免在解析类型内的表达式时需要无限前瞻。

#![allow(unused)]
fn main() {
fn double<const N: i32>() {
    println!("doubled: {}", N * 2);
}

const SOME_CONST: i32 = 12;

fn example() {
    // const 参数的使用示例。
    double::<9>();
    double::<-123>();
    double::<{7 + 8}>();
    double::<SOME_CONST>();
    double::<{ SOME_CONST + 5 }>();
}
}

当一个泛型参数可以同时被解析为类型或常量参数时,将总是被解析为类型参数。 将该参数放在块表达式中可以强制将其解释为常量参数。

#![allow(unused)]
fn main() {
type N = u32;
struct Foo<const N: usize>;
// 以下是一个错误,因为 `N` 被解释为类型别名 `N` 。
fn foo<const N: usize>() -> Foo<N> { todo!() } // ERROR
// 可以用大括号包起来,强制解释为 `N` 常量参数。
fn bar<const N: usize>() -> Foo<{ N }> { todo!() } // ok
}

与类型和生命周期参数不同,常量参数可以在不在参数化条目内部使用的情况下声明,但在实现中除外,如 泛型实现 所述:

#![allow(unused)]
fn main() {
// 成功
struct Foo<const N: usize>;
enum Bar<const M: usize> { A, B }

// 错误: 未使用的参数
struct Baz<T>;
struct Biz<'a>;
struct Unconstrained;
impl<const N: usize> Unconstrained {}
}

在解析 trait 约束职责时,在确定约束是否满足时,不考虑所有常量参数的实现是否穷尽。 例如,即使为 bool 类型实现了所有可能的常量值,但仍然会出错,因为 trait 约束未满足:

#![allow(unused)]
fn main() {
struct Foo<const B: bool>;
trait Bar {}
impl Bar for Foo<true> {}
impl Bar for Foo<false> {}

fn needs_bar(_: impl Bar) {}
fn generic<const B: bool>() {
    let v = Foo::<B>;
    needs_bar(v); // ERROR: trait 约束 `Foo<B>: Bar` 不满足
}
}

Where 从句

语法
Where从句 :
   where ( Where从句条目 , )* Where从句条目 ?

Where从句条目 :
      生命周期Where从句条目
   | 类型约束Where从句条目

生命周期Where从句条目 :
   生命周期 : 生命周期约束组

类型约束Where从句条目 :
   [对于生命周期组]? 类型 : 类型参数约束组?

where 从句 提供了另一种方法来指定类型和生命周期参数的约束,以及指定非类型参数的类型约束的方法。

for 关键字可用于引入 更高阶生命周期 ,仅允许 生命周期参数

#![allow(unused)]
fn main() {
struct A<T>
where
    T: Iterator,            // 可以用 A<T: Iterator> 代替
    T::Item: Copy,          // 绑定在一个关联类型上
    String: PartialEq<T>,   // 绑定在 `String` 上,使用类型参数
    i32: Default,           // 允许的,但没有用
{
    f: T,
}
}

属性

泛型生命周期和类型参数允许 属性 。在这个位置上没有内置属性起作用,尽管自定义派生属性可能会赋予其意义。

此示例展示使用自定义衍生属性来修改泛型参数的含义。

// 假设 MyFlexibleClone 衍生声明了 `my_flexible_clone` 作为它所理解的一个属性。
#[derive(MyFlexibleClone)]
struct Foo<#[my_flexible_clone(unbounded)] H> {
    a: *const H
}