rust-by-practice/zh-CN/src/generics-traits/const-generics.md

137 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Const 泛型
在之前的泛型中,可以抽象为一句话:针对类型实现的泛型,所有的泛型都是为了抽象不同的类型,那有没有针对值的泛型?答案就是 `Const 泛型`
## 示例
1. 下面的例子同时使用泛型和 const 泛型来实现一个结构体,该结构体的字段中的数组长度是可变的
```rust,editable
struct ArrayPair<T, const N: usize> {
left: [T; N],
right: [T; N],
}
impl<T: Debug, const N: usize> Debug for ArrayPair<T, N> {
// ...
}
```
2. 目前const 泛型参数只能使用以下形式的实参:
- 一个单独的 const 泛型参数
- 一个字面量 (i.e. 整数, 布尔值或字符).
- 一个具体的 const 表达式( 表达式中不能包含任何 泛型参数)
```rust,editable
fn foo<const N: usize>() {}
fn bar<T, const M: usize>() {
foo::<M>(); // ok: 符合第一种
foo::<2021>(); // ok: 符合第二种
foo::<{20 * 100 + 20 * 10 + 1}>(); // ok: 符合第三种
foo::<{ M + 1 }>(); // error: 违背第三种const 表达式中不能有泛型参数 M
foo::<{ std::mem::size_of::<T>() }>(); // error: 泛型表达式包含了泛型参数 T
let _: [u8; M]; // ok: 符合第一种
let _: [u8; std::mem::size_of::<T>()]; // error: 泛型表达式包含了泛型参数 T
}
fn main() {}
```
1. const 泛型还能帮我们避免一些运行时检查,提升性能
```rust
pub struct MinSlice<T, const N: usize> {
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::<u8, 12>::from_slice(slice).unwrap();
let value: u8 = minslice.head[6];
assert_eq!(value, b' ')
}
```
## 练习
1. 🌟🌟 `<T, const N: usize>` 是结构体类型的一部分,和数组类型一样,这意味着长度不同会导致类型不同: `Array<i32, 3>``Array<i32, 4>` 是不同的类型
```rust,editable
// 修复错误
struct Array<T, const N: usize> {
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<T>(val: T)
where
Assert<{ core::mem::size_of::<T>() < 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<const CHECK: bool> {}
pub trait IsTrue {}
impl IsTrue for Assert<true> {}
```