# Const 泛型 在之前的泛型中,可以抽象为一句话:针对类型实现的泛型,所有的泛型都是为了抽象不同的类型,那有没有针对值的泛型?答案就是 `Const 泛型`。 ## 示例 1. 下面的例子同时使用泛型和 const 泛型来实现一个结构体,该结构体的字段中的数组长度是可变的 ```rust,editable struct ArrayPair { left: [T; N], right: [T; N], } impl Debug for ArrayPair { // ... } ``` 2. 目前,const 泛型参数只能使用以下形式的实参: - 一个单独的 const 泛型参数 - 一个字面量 (i.e. 整数, 布尔值或字符). - 一个具体的 const 表达式( 表达式中不能包含任何 泛型参数) ```rust,editable fn foo() {} fn bar() { foo::(); // ok: 符合第一种 foo::<2021>(); // ok: 符合第二种 foo::<{20 * 100 + 20 * 10 + 1}>(); // ok: 符合第三种 foo::<{ M + 1 }>(); // error: 违背第三种,const 表达式中不能有泛型参数 M foo::<{ std::mem::size_of::() }>(); // error: 泛型表达式包含了泛型参数 T let _: [u8; M]; // ok: 符合第一种 let _: [u8; std::mem::size_of::()]; // error: 泛型表达式包含了泛型参数 T } fn main() {} ``` 3. const 泛型还能帮我们避免一些运行时检查,提升性能 ```rust pub struct MinSlice { pub head: [T; N], pub tail: [T], } fn main() { let slice: &[u8] = b"Hello, world"; let reference: Option<&u8> = slice.get(6); // 我们知道 `.get` 返回的是 `Some(b' ')` // 但编译器不知道 assert!(reference.is_some()); let slice: &[u8] = b"Hello, world"; // 当编译构建 MinSlice 时会进行长度检查,也就是在编译期我们就知道它的长度是 12 // 在运行期,一旦 `unwrap` 成功,在 `MinSlice` 的作用域内,就再无需任何检查 let minslice = MinSlice::::from_slice(slice).unwrap(); let value: u8 = minslice.head[6]; assert_eq!(value, b' ') } ``` ## 练习 1. 🌟🌟 `` 是结构体类型的一部分,和数组类型一样,这意味着长度不同会导致类型不同: `Array` 和 `Array` 是不同的类型 ```rust,editable // 修复错误 struct Array { data : [T; N] } fn main() { let arrays = [ Array{ data: [1, 2, 3], }, Array { data: [1.0, 2.0, 3.0], }, Array { data: [1, 2] } ]; } ``` 2. 🌟🌟 ```rust,editable // 填空 fn print_array<__>(__) { println!("{:?}", arr); } fn main() { let arr = [1, 2, 3]; print_array(arr); let arr = ["hello", "world"]; print_array(arr); } ``` 3. 🌟🌟🌟 有时我们希望能限制一个变量占用内存的大小,例如在嵌入式环境中,此时 const 泛型参数的第三种形式 `const 表达式` 就非常适合. ```rust,editable #![allow(incomplete_features)] #![feature(generic_const_exprs)] fn check_size(val: T) where Assert<{ core::mem::size_of::() < 768 }>: IsTrue, { //... } // 修复 main 函数中的错误 fn main() { check_size([0u8; 767]); check_size([0i32; 191]); check_size(["hello你好"; __]); // size of &str ? check_size([(); __].map(|_| "hello你好".to_string())); // size of String? check_size(['中'; __]); // size of char ? } pub enum Assert {} pub trait IsTrue {} impl IsTrue for Assert {} ``` > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/generics-traits/const-generics.md)找到答案(在 solutions 路径下)