泛型参数
Syntax
GenericParams → < ( GenericParam ( , GenericParam )* ,? )? >
GenericParam → OuterAttribute* ( LifetimeParam | TypeParam | ConstParam )
LifetimeParam → Lifetime ( : LifetimeBounds )?
TypeParam → IDENTIFIER ( : Bounds? )? ( = Type )?
ConstParam →
const IDENTIFIER : Type
( = ( BlockExpression | IDENTIFIER | -? LiteralExpression ) )?
函数、类型别名、结构体、枚举、联合体、trait 和实现 可以通过类型、常量和生命周期进行参数化。这些参数列在尖括号(<...>)中,通常紧跟在项的名称之后、其定义之前。对于没有名称的实现,它们直接跟在 impl 之后。
泛型参数的顺序仅限于生命周期参数,然后是类型和 const 参数的混合。
在 GenericParams 列表中不能多次声明相同的参数名称。
具有类型、const 和生命周期参数的项的一些示例:
#![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 泛型
Const 泛型参数允许项对常量值进行泛型。
const 标识符在值命名空间中为常量参数引入一个名称,项的所有实例必须使用给定类型的值实例化。
唯一允许的 const 参数类型是 u8、u16、u32、u64、u128、usize、i8、i16、i32、i64、i128、isize、char 和 bool。
Const 参数可以在常量项可以使用的任何地方使用,但在类型或数组重复表达式中使用时,它必须是独立的(如下所述)。也就是说,它们在以下位置被允许:
- 作为应用于任何类型的已应用 const,该类型构成相关项签名的一部分。
- 作为用于定义关联常量的 const 表达式的一部分,或作为关联类型的参数。
- 作为项中任何函数主体中任何运行时表达式的值。
- 作为项中任何函数主体中使用的任何类型的参数。
- 作为项中任何字段的类型的一部分。
#![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]);
}
}
作为进一步的限制,const 参数只能作为独立参数出现在类型或数组重复表达式内部。在这些上下文中,它们只能用作单个段路径表达式,可能在块内部(如 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 或归因于 const 参数的类型的常量表达式。常量表达式必须是[块表达式][block expression](用大括号括起来),除非它是单个路径段(IDENTIFIER)或字面量(可能带有前导 - 词法单元)。
Note
此语法限制是必要的,以避免在解析类型内的表达式时需要无限前瞻。
#![allow(unused)]
fn main() {
struct S<const N: i64>;
const C: i64 = 1;
fn f<const N: i64>() -> S<N> { S }
let _ = f::<1>(); // Literal.
let _ = f::<-1>(); // Negative literal.
let _ = f::<{ 1 + 2 }>(); // Constant expression.
let _ = f::<C>(); // Single segment path.
let _ = f::<{ C + 1 }>(); // Constant expression.
let _: S<1> = f::<_>(); // Inferred const.
let _: S<1> = f::<(((_)))>(); // Inferred const.
}
Note
在泛型参数列表中,推断 const 被解析为推断类型,但在语义上被视为单独的const 泛型参数。
在期望 const 参数的地方,可以使用 _(可选地用任意数量的匹配括号括起来),称为推断 const(路径规则,数组表达式规则)。这要求编译器根据周围信息推断 const 参数(如果可能)。
#![allow(unused)]
fn main() {
fn make_buf<const N: usize>() -> [u8; N] {
[0; _]
// ^ Infers `N`.
}
let _: [u8; 1024] = make_buf::<_>();
// ^ Infers `1024`.
}
Note
推断 const 在语义上不是表达式,因此在大括号内不被接受。
#![allow(unused)] fn main() { fn f<const N: usize>() -> [u8; N] { [0; _] } let _: [_; 1] = f::<{ _ }>(); // ^ ERROR `_` not allowed here }
推断 const 不能在项签名中使用。
#![allow(unused)]
fn main() {
fn f<const N: usize>(x: [u8; N]) -> [u8; _] { x }
// ^ ERROR not allowed
}
当存在歧义,即泛型参数可以解析为类型或 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 {}
}
解析 trait 约束义务时,在确定约束是否满足时不考虑 const 参数的所有实现的穷举性。例如,在以下示例中,即使实现了 bool 类型的所有可能 const 值,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 bound `Foo<B>: Bar` is not satisfied
}
}
Where 子句
Syntax
WhereClause → where ( WhereClauseItem , )* WhereClauseItem?
WhereClauseItem →
LifetimeWhereClauseItem
| TypeBoundWhereClauseItem
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
}