泛型参数

语法
GenericParams :
      < >
   | < (GenericParam ,)* GenericParam ,? >

GenericParam :
   OuterAttribute* ( LifetimeParam | TypeParam | ConstParam )

LifetimeParam :
   Lifetime ( : LifetimeBounds )?

TypeParam :
   IDENTIFIER ( : TypeParamBounds? )? ( = Type )?

ConstParam:
   const IDENTIFIER : Type ( = Block | IDENTIFIER | -?LITERAL )?

函数类型别名结构体枚举联合体trait实现 可以通过类型、常量和生命周期进行参数化。这些参数列在尖括号 <...> 中,通常紧跟在项的名称之后,并在其定义之前。对于没有名称的实现,它们直接出现在 impl 之后。

泛型参数的顺序被限制为先是生命周期参数,然后是类型和常量参数混合排列。

GenericParams 列表中,同一个参数名称不能声明多次。

带有类型、常量和生命周期参数的项的一些示例

#![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);
}

泛型参数在其声明的项定义范围内有效。它们在函数体中声明的项的范围内无效,如 项声明 中所述。有关更多详细信息,请参阅 泛型参数作用域

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

'_'_static 不是有效的生命周期参数。

常量泛型

常量泛型参数 允许项在常量值上是泛型的。

const 标识符在 值命名空间 中为常量参数引入一个名称,并且项的所有实例都必须使用给定类型的值实例化。

常量参数唯一允许的类型是 u8u16u32u64u128usizei8i16i32i64i128isizecharbool

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

  1. 作为应用于构成问题项签名一部分的任何类型的常量。
  2. 作为用于定义 关联常量 的常量表达式的一部分,或作为 关联类型 的参数。
  3. 作为项中任何函数体中任何运行时表达式中的值。
  4. 作为项中任何函数体中使用的任何类型的参数。
  5. 作为项中任何字段类型的一部分。
#![allow(unused)]
fn main() {
// Examples where const generic parameters can be used.

// Used in the signature of the item itself.
fn foo<const N: usize>(arr: [i32; N]) {
    // Used as a type within a function body.
    let x: [i32; N];
    // Used as an expression.
    println!("{}", N * 2);
}

// Used as a field of a struct.
struct Foo<const N: usize>([i32; N]);

impl<const N: usize> Foo<N> {
    // Used as an associated constant.
    const CONST: usize = N * 4;
}

trait Trait {
    type Output;
}

impl<const N: usize> Trait for Foo<N> {
    // Used as an associated type.
    type Output = [i32; N];
}
}
#![allow(unused)]
fn main() {
// Examples where const generic parameters cannot be used.
fn foo<const N: usize>() {
    // Cannot use in item definitions within a function body.
    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() {
// Examples where const parameters may not be used.

// Not allowed to combine in other expressions in types, such as the
// arithmetic expression in the return type here.
fn bad_function<const N: usize>() -> [u8; {N + 1}] {
    // Similarly not allowed for array repeat expressions.
    [1; {N + 1}]
}
}

路径 中的常量参数指定要用于该项的常量值。

参数必须是分配给常量参数类型的 常量表达式。常量表达式必须是 代码块表达式(用大括号括起来),除非它是单个路径段(IDENTIFIER)或 字面量(可能带有前导 - 标记)。

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

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

const SOME_CONST: i32 = 12;

fn example() {
    // Example usage of a const argument.
    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>;
// The following is an error, because `N` is interpreted as the type alias `N`.
fn foo<const N: usize>() -> Foo<N> { todo!() } // ERROR
// Can be fixed by wrapping in braces to force it to be interpreted as the `N`
// const parameter:
fn bar<const N: usize>() -> Foo<{ N }> { todo!() } // ok
}

与类型和生命周期参数不同,常量参数可以声明为不用于参数化项内部,但 泛型实现 中描述的实现除外。

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

// ERROR: unused parameter
struct Baz<T>;
struct Biz<'a>;
struct Unconstrained;
impl<const N: usize> Unconstrained {}
}

在解析 trait bound 义务时,在确定 bound 是否满足时,不考虑常量参数的所有实现的穷尽性。例如,在以下示例中,即使实现了 bool 类型的所有可能的常量值,trait bound 仍然不满足,因此仍然会报错

#![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 bound `Foo<B>: Bar` is not satisfied
}
}

Where 子句

语法
WhereClause :
   where ( WhereClauseItem , )* WhereClauseItem ?

WhereClauseItem :
      LifetimeWhereClauseItem
   | TypeBoundWhereClauseItem

LifetimeWhereClauseItem :
   Lifetime : LifetimeBounds

TypeBoundWhereClauseItem :
   ForLifetimes? Type : TypeParamBounds?

Where 子句 提供了另一种指定类型和生命周期参数的 bound 的方法,以及指定非类型参数类型的 bound 的方法。

for 关键字可用于引入 高阶生命周期。它只允许 LifetimeParam 参数。

#![allow(unused)]
fn main() {
struct A<T>
where
    T: Iterator,            // Could use A<T: Iterator> instead
    T::Item: Copy,          // Bound on an associated type
    String: PartialEq<T>,   // Bound on `String`, using the type parameter
    i32: Default,           // Allowed, but not useful
{
    f: T,
}
}

属性

泛型生命周期和类型参数允许在其上添加 属性。在此位置没有任何内置属性执行任何操作,尽管自定义派生属性可能会赋予其含义。

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

// Assume that the derive for MyFlexibleClone declared `my_flexible_clone` as
// an attribute it understands.
#[derive(MyFlexibleClone)]
struct Foo<#[my_flexible_clone(unbounded)] H> {
    a: *const H
}