add const generics

This commit is contained in:
sunface
2022-03-04 16:00:42 +08:00
parent fe6d0b4183
commit 029fdd1a24
5 changed files with 343 additions and 1 deletions

View File

@ -0,0 +1,64 @@
1.
```rust
struct Array<T, const N: usize> {
data : [T; N]
}
fn main() {
let arrays = [
Array{
data: [1, 2, 3],
},
Array {
data: [1, 2, 3],
},
Array {
data: [1, 2,4]
}
];
}
```
2.
```rust
fn print_array<T: std::fmt::Debug, const N: usize>(arr: [T; N]) {
println!("{:?}", arr);
}
fn main() {
let arr = [1, 2, 3];
print_array(arr);
let arr = ["hello", "world"];
print_array(arr);
}
```
3.
```rust
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
fn check_size<T>(val: T)
where
Assert<{ core::mem::size_of::<T>() < 768 }>: IsTrue,
{
//...
}
// fix the errors in main
fn main() {
check_size([0u8; 767]);
check_size([0i32; 191]);
check_size(["hello你好"; 47]); // &str is a string reference, containing a pointer and string length in it, so it takes two word long, in x86-64, 1 word = 8 bytes
check_size(["hello你好".to_string(); 31]); // String is a smart pointer struct, it has three fields: pointer, length and capacity, each takes 8 bytes
check_size(['中'; 191]); // A char takes 4 bytes in Rust
}
pub enum Assert<const CHECK: bool> {}
pub trait IsTrue {}
impl IsTrue for Assert<true> {}
```

View File

@ -24,6 +24,7 @@
- [Method & Associated function](method.md) - [Method & Associated function](method.md)
- [Generics and Traits](generics-traits/intro.md) - [Generics and Traits](generics-traits/intro.md)
- [Generics](generics-traits/generics.md) - [Generics](generics-traits/generics.md)
- [Const Generics](generics-traits/const-generics.md)
- [Traits](generics-traits/traits.md) - [Traits](generics-traits/traits.md)
- [Trait Object](generics-traits/trait-object.md) - [Trait Object](generics-traits/trait-object.md)
- [Advance Traits](generics-traits/advance-traits.md) - [Advance Traits](generics-traits/advance-traits.md)

View File

@ -0,0 +1,139 @@
# Const Generics
Const generics are generic arguments that range over constant values, rather than types or lifetimes. This allows, for instance, types to be parameterized by integers. In fact, there has been one example of const generic types since early on in Rust's development: the array types [T; N], for some type T and N: usize. However, there has previously been no way to abstract over arrays of an arbitrary size: if you wanted to implement a trait for arrays of any size, you would have to do so manually for each possible value. For a long time, even the standard library methods for arrays were limited to arrays of length at most 32 due to this problem.
## Examples
1. Here's an example of a type and implementation making use of const generics: a type wrapping a pair of arrays of the same size.
```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. Currently, const parameters may only be instantiated by const arguments of the following forms:
- A standalone const parameter.
- A literal (i.e. an integer, bool, or character).
- A concrete constant expression (enclosed by {}), involving no generic parameters.
```rust,editable
fn foo<const N: usize>() {}
fn bar<T, const M: usize>() {
foo::<M>(); // ok: `M` is a const parameter
foo::<2021>(); // ok: `2021` is a literal
foo::<{20 * 100 + 20 * 10 + 1}>(); // ok: const expression contains no generic parameters
foo::<{ M + 1 }>(); // error: const expression contains the generic parameter `M`
foo::<{ std::mem::size_of::<T>() }>(); // error: const expression contains the generic parameter `T`
let _: [u8; M]; // ok: `M` is a const parameter
let _: [u8; std::mem::size_of::<T>()]; // error: const expression contains the generic parameter `T`
}
fn main() {}
```
3. Const generics can also let us avoid some runtime checks.
```rust
/// A region of memory containing at least `N` `T`s.
pub struct MinSlice<T, const N: usize> {
/// The bounded region of memory. Exactly `N` `T`s.
pub head: [T; N],
/// Zero or more remaining `T`s after the `N` in the bounded region.
pub tail: [T],
}
fn main() {
let slice: &[u8] = b"Hello, world";
let reference: Option<&u8> = slice.get(6);
// We know this value is `Some(b' ')`,
// but the compiler can't know that.
assert!(reference.is_some());
let slice: &[u8] = b"Hello, world";
// Length check is performed when we construct a MinSlice,
// and it's known at compile time to be of length 12.
// If the `unwrap()` succeeds, no more checks are needed
// throughout the `MinSlice`'s lifetime.
let minslice = MinSlice::<u8, 12>::from_slice(slice).unwrap();
let value: u8 = minslice.head[6];
assert_eq!(value, b' ')
}
```
## Exercises
1. 🌟🌟 `<T, const N: usize>` is part of the struct type, it means `Array<i32, 3>` and `Array<i32, 4>` are different types.
```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
// fill in the blanks to make it work
fn print_array<__>(__) {
println!("{:?}", arr);
}
fn main() {
let arr = [1, 2, 3];
print_array(arr);
let arr = ["hello", "world"];
print_array(arr);
}
```
3. 🌟🌟🌟 Sometimes we want to limit the size of an variable, e.g when using in embedding evironments, then `const expressions` will fit your need.
```rust,editable
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
fn check_size<T>(val: T)
where
Assert<{ core::mem::size_of::<T>() < 768 }>: IsTrue,
{
//...
}
// fix the errors in main
fn main() {
check_size([0u8; 767]);
check_size([0i32; 191]);
check_size(["hello你好"; __]); // size of &str ?
check_size(["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> {}
```

View File

@ -23,7 +23,8 @@
- [模式](pattern-match/patterns.md) - [模式](pattern-match/patterns.md)
- [方法和关联函数](method.md) - [方法和关联函数](method.md)
- [泛型和特征](generics-traits/intro.md) - [泛型和特征](generics-traits/intro.md)
- [泛型 Generics](generics-traits/generics.md) - [泛型](generics-traits/generics.md)
- [Const 泛型](generics-traits/const-generics.md)
- [特征 Traits](generics-traits/traits.md) - [特征 Traits](generics-traits/traits.md)
- [特征对象](generics-traits/trait-object.md) - [特征对象](generics-traits/trait-object.md)
- [进一步深入特征](generics-traits/advance-traits.md) - [进一步深入特征](generics-traits/advance-traits.md)

View File

@ -0,0 +1,137 @@
# 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(["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> {}
```