泛型参数
语法
GenericParams :
<
>
|<
(GenericParam,
)* GenericParam,
?>
GenericParam :
OuterAttribute* ( LifetimeParam | TypeParam | ConstParam )LifetimeParam :
LIFETIME_OR_LABEL (:
LifetimeBounds )?TypeParam :
IDENTIFIER(:
TypeParamBounds? )? (=
Type )?ConstParam:
const
IDENTIFIER:
Type (=
Block | IDENTIFIER | -?LITERAL )?
函数、类型别名、结构体、枚举、联合体、特征和实现可以通过类型、常量和生命周期进行参数化。这些参数列在尖括号(<...>
)中,通常紧跟在项目名称之后、定义之前。对于没有名称的实现,它们直接位于 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 标识符引入了常量参数的名称,并且该项目的所有实例都必须使用给定类型的值进行实例化。
唯一允许的常量参数类型是 u8
、u16
、u32
、u64
、u128
、usize
、i8
、i16
、i32
、i64
、i128
、isize
、char
和 bool
。
常量参数可以在可以使用常量项的任何地方使用,但当在类型或数组重复表达式中使用时,它必须是独立的(如下所述)。也就是说,它们允许在以下位置使用
- 作为应用于构成相关项目签名一部分的任何类型的应用常量。
- 作为用于定义关联常量的常量表达式的一部分,或作为关联类型的参数。
- 作为项目中任何函数体中任何运行时表达式中的值。
- 作为项目中任何函数体中使用的任何类型的参数。
- 作为项目中任何字段类型的一部分。
#![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}] } }
在 路径 中的 const 参数指定了用于该项的常量值。该参数必须是赋予 const 参数类型的 常量表达式。除非 const 表达式是单个路径段(一个 标识符)或 字面量(可能带有前导 -
符号),否则它必须是一个 块表达式(用大括号括起来)。
**注意**:此语法限制是为了避免在解析类型内部的表达式时需要无限向前查看。
#![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 }>(); } }
当无法确定泛型参数是应该解析为类型参数还是 const 参数时,它将始终解析为类型参数。将参数放在块表达式中可以强制将其解释为 const 参数。
#![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 }
与类型参数和生命周期参数不同,const 参数可以在参数化项内部声明而不使用,但 泛型实现 中描述的实现除外。
#![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 {} }
在解析特征绑定义务时,确定绑定是否满足时,不会考虑 const 参数的所有实现的穷尽性。例如,在以下代码中,即使实现了 bool
类型的所有可能的 const 值,但特征绑定不满足仍然是一个错误。
#![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
| TypeBoundWhereClauseItemLifetimeWhereClauseItem :
生命周期:
生命周期边界TypeBoundWhereClauseItem :
ForLifetimes? 类型:
类型参数边界?
Where 子句 提供了另一种指定类型参数和生命周期参数边界的方法,以及指定非类型参数的类型边界的方法。
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
}