泛型参数
语法
泛型参数组 :
<
>
|<
(泛型参数,
)* 泛型参数,
?>
泛型参数 :
外围属性* ( 生命周期参数 | 类型参数 | 常量参数 )生命周期参数 :
生命周期或标签 (:
生命周期约束组 )?
函数、 类型别名 、 结构体 、 枚举 、 联合体 、 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 标识符引入了常量参数的名称,并且必须使用给定类型的值来实例化条目实例。
仅允许的常量参数类型有 u8
、 u16
、 u32
、 u64
、 u128
、 usize
、i8
、 i16
、 i32
、 i64
、 i128
、 isize
、 char
、 bool
.
常量参数可以在任何 常量条目 可用的地方使用,但是当常量参数用于 类型 或 数组重复表达式 时,必须是独立的 (如下所述) 。也就是说,允许出现在以下位置:
- 作为相关条目签名一部分的类型应用 const 。
- 作为定义 关联常量 或作为 关联类型 参数的常量表达式的一部分。
- 作为条目中任何函数体中运行时表达式中的值。
- 作为条目中任何函数体中使用的任何类型的参数。
- 作为条目字段类型的一部分。
#![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 从句 提供了另一种方法来指定类型和生命周期参数的约束,以及指定非类型参数的类型约束的方法。
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
}