Skip to content

Commit 9db703b

Browse files
committed
git commit -m "feat: Add exercises for new Rust features
Add exercises for: - const generics - let-else statements - generic associated types (GAT) - async traits These exercises help learners understand and practice new Rust features introduced in recent versions."
1 parent e36dd7a commit 9db703b

File tree

6 files changed

+341
-0
lines changed

6 files changed

+341
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" }
5454
serde_json = "1.0"
5555
serde.workspace = true
5656
toml_edit.workspace = true
57+
tokio = { version = "1.36", features = ["full"] }
5758

5859
[target.'cfg(not(windows))'.dependencies]
5960
rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] }
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// const_generics.rs
2+
//
3+
// Const 泛型允许我们使用编译时常量作为泛型参数。这在处理固定大小的数组和其他
4+
// 需要编译时常量的场景特别有用。
5+
//
6+
// 在这个练习中,我们将实现一个简单的固定大小数组包装器,它可以安全地访问数组元素
7+
// 并提供一些实用的方法。
8+
9+
#![allow(dead_code)]
10+
11+
// TODO: 实现一个泛型结构体 FixedArray<T, const N: usize>
12+
// 它应该包装一个固定大小的数组 [T; N]
13+
struct FixedArray<T, const N: usize> {
14+
data: [T; N],
15+
}
16+
17+
impl<T, const N: usize> FixedArray<T, N> {
18+
// TODO: 实现 new 方法,它接受一个数组并返回 FixedArray
19+
fn new(arr: [T; N]) -> Self {
20+
todo!("创建一个新的 FixedArray 实例")
21+
}
22+
23+
// TODO: 实现 get 方法,它安全地返回索引处的元素引用
24+
fn get(&self, index: usize) -> Option<&T> {
25+
todo!("返回指定索引处的元素,如果索引越界则返回 None")
26+
}
27+
28+
// TODO: 实现 len 方法,返回数组的长度
29+
fn len(&self) -> usize {
30+
todo!("返回数组的长度")
31+
}
32+
}
33+
34+
#[cfg(test)]
35+
mod tests {
36+
use super::*;
37+
38+
#[test]
39+
fn test_fixed_array() {
40+
let arr = FixedArray::new([1, 2, 3, 4, 5]);
41+
42+
// 测试长度
43+
assert_eq!(arr.len(), 5);
44+
45+
// 测试有效索引
46+
assert_eq!(arr.get(0), Some(&1));
47+
assert_eq!(arr.get(4), Some(&5));
48+
49+
// 测试无效索引
50+
assert_eq!(arr.get(5), None);
51+
}
52+
53+
#[test]
54+
fn test_different_types() {
55+
let arr = FixedArray::new(["hello", "world"]);
56+
assert_eq!(arr.len(), 2);
57+
assert_eq!(arr.get(0), Some(&"hello"));
58+
assert_eq!(arr.get(1), Some(&"world"));
59+
}
60+
61+
#[test]
62+
fn test_empty_array() {
63+
let arr: FixedArray<i32, 0> = FixedArray::new([]);
64+
assert_eq!(arr.len(), 0);
65+
assert_eq!(arr.get(0), None);
66+
}
67+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// let_else.rs
2+
//
3+
// let-else 语句是 Rust 1.65 中引入的新特性。它允许我们在模式匹配失败时
4+
// 提前返回或中断执行。这个特性特别适合于处理 Option 和 Result 类型。
5+
//
6+
// 在这个练习中,我们将使用 let-else 语句来简化错误处理代码。
7+
8+
#[derive(Debug, PartialEq)]
9+
struct Point {
10+
x: i32,
11+
y: i32,
12+
}
13+
14+
#[derive(Debug, PartialEq)]
15+
struct Rectangle {
16+
top_left: Point,
17+
bottom_right: Point,
18+
}
19+
20+
impl Rectangle {
21+
// TODO: 使用 let-else 语句实现这个函数
22+
// 如果参数无效(左上角点的坐标大于右下角点的坐标),返回 None
23+
fn new(top_left: Point, bottom_right: Point) -> Option<Rectangle> {
24+
todo!("实现 Rectangle::new,使用 let-else 语句验证参数")
25+
}
26+
27+
// TODO: 使用 let-else 语句实现这个函数
28+
// 函数应该解析字符串格式的矩形定义,格式为 "x1,y1,x2,y2"
29+
// 其中 x1,y1 是左上角坐标,x2,y2 是右下角坐标
30+
fn parse(s: &str) -> Option<Rectangle> {
31+
todo!("实现字符串解析为 Rectangle 的功能")
32+
}
33+
}
34+
35+
#[cfg(test)]
36+
mod tests {
37+
use super::*;
38+
39+
#[test]
40+
fn test_valid_rectangle() {
41+
let rect = Rectangle::new(
42+
Point { x: 0, y: 0 },
43+
Point { x: 10, y: 10 }
44+
);
45+
assert!(rect.is_some());
46+
}
47+
48+
#[test]
49+
fn test_invalid_rectangle() {
50+
let rect = Rectangle::new(
51+
Point { x: 10, y: 10 },
52+
Point { x: 0, y: 0 }
53+
);
54+
assert!(rect.is_none());
55+
}
56+
57+
#[test]
58+
fn test_parse_valid() {
59+
let rect = Rectangle::parse("0,0,10,10");
60+
assert_eq!(rect, Some(Rectangle {
61+
top_left: Point { x: 0, y: 0 },
62+
bottom_right: Point { x: 10, y: 10 }
63+
}));
64+
}
65+
66+
#[test]
67+
fn test_parse_invalid_format() {
68+
assert!(Rectangle::parse("invalid").is_none());
69+
assert!(Rectangle::parse("0,0,10").is_none());
70+
assert!(Rectangle::parse("0,0,10,10,12").is_none());
71+
}
72+
73+
#[test]
74+
fn test_parse_invalid_coordinates() {
75+
assert!(Rectangle::parse("10,10,0,0").is_none());
76+
}
77+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// gat.rs
2+
//
3+
// GAT (Generic Associated Types) 是 Rust 1.65 中引入的一个强大特性。
4+
// 它允许在关联类型中使用泛型参数,这在创建容器类型和迭代器时特别有用。
5+
//
6+
// 在这个练习中,我们将实现一个简单的 Map 容器,它可以存储不同类型的值
7+
// 并提供类型安全的访问方法。
8+
9+
#![allow(dead_code)]
10+
11+
// 定义一个特征,表示可以存储和检索值的容器
12+
trait Container {
13+
// TODO: 使用 GAT 定义一个关联类型 Value,它有一个生命周期参数
14+
type Value<'a>: 'a
15+
where
16+
Self: 'a;
17+
18+
// 获取容器中的值的引用
19+
fn get<'a>(&'a self) -> Self::Value<'a>;
20+
}
21+
22+
// 一个简单的包装类型
23+
struct Wrapper<T>(T);
24+
25+
// TODO: 为 Wrapper<T> 实现 Container 特征
26+
// Value 类型应该是对 T 的引用
27+
impl<T> Container for Wrapper<T> {
28+
type Value<'a> = todo!("定义正确的关联类型");
29+
30+
fn get<'a>(&'a self) -> Self::Value<'a> {
31+
todo!("返回对内部值的引用")
32+
}
33+
}
34+
35+
// 一个选项包装类型
36+
struct OptionWrapper<T>(Option<T>);
37+
38+
// TODO: 为 OptionWrapper<T> 实现 Container 特征
39+
// Value 类型应该是 Option<&T>
40+
impl<T> Container for OptionWrapper<T> {
41+
type Value<'a> = todo!("定义正确的关联类型");
42+
43+
fn get<'a>(&'a self) -> Self::Value<'a> {
44+
todo!("返回 Option<&T>")
45+
}
46+
}
47+
48+
#[cfg(test)]
49+
mod tests {
50+
use super::*;
51+
52+
#[test]
53+
fn test_wrapper() {
54+
let w = Wrapper(42);
55+
assert_eq!(*w.get(), 42);
56+
}
57+
58+
#[test]
59+
fn test_option_wrapper_some() {
60+
let w = OptionWrapper(Some(42));
61+
assert_eq!(w.get(), Some(&42));
62+
}
63+
64+
#[test]
65+
fn test_option_wrapper_none() {
66+
let w: OptionWrapper<i32> = OptionWrapper(None);
67+
assert_eq!(w.get(), None);
68+
}
69+
70+
// 这个测试确保我们的实现可以处理不同的生命周期
71+
#[test]
72+
fn test_lifetime() {
73+
let w = Wrapper(String::from("hello"));
74+
let r: &String = w.get();
75+
assert_eq!(r, "hello");
76+
}
77+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// async_trait.rs
2+
//
3+
// 异步特征是 Rust 1.75 中稳定的新特性。它允许在特征中直接定义异步方法,
4+
// 不再需要使用 async-trait 宏。这个特性大大简化了异步编程的代码。
5+
//
6+
// 在这个练习中,我们将实现一个简单的异步数据获取接口。
7+
8+
#![allow(dead_code)]
9+
10+
use std::time::Duration;
11+
12+
// 模拟一个数据源
13+
struct DataSource {
14+
data: Vec<String>,
15+
}
16+
17+
impl DataSource {
18+
fn new() -> Self {
19+
Self {
20+
data: vec![
21+
"Hello".to_string(),
22+
"World".to_string(),
23+
"Rust".to_string(),
24+
],
25+
}
26+
}
27+
}
28+
29+
// TODO: 实现一个异步特征 AsyncDataFetcher
30+
// 它应该包含以下异步方法:
31+
// - fetch_data: 获取指定索引的数据
32+
// - fetch_all: 获取所有数据
33+
// - count: 获取数据总数
34+
trait AsyncDataFetcher {
35+
async fn fetch_data(&self, index: usize) -> Option<String>;
36+
async fn fetch_all(&self) -> Vec<String>;
37+
async fn count(&self) -> usize;
38+
}
39+
40+
// TODO: 为 DataSource 实现 AsyncDataFetcher 特征
41+
impl AsyncDataFetcher for DataSource {
42+
async fn fetch_data(&self, index: usize) -> Option<String> {
43+
todo!("模拟异步获取数据,使用 tokio::time::sleep 增加延迟")
44+
}
45+
46+
async fn fetch_all(&self) -> Vec<String> {
47+
todo!("模拟异步获取所有数据")
48+
}
49+
50+
async fn count(&self) -> usize {
51+
todo!("模拟异步获取数据数量")
52+
}
53+
}
54+
55+
#[cfg(test)]
56+
mod tests {
57+
use super::*;
58+
use tokio::time::sleep;
59+
60+
#[tokio::test]
61+
async fn test_fetch_data() {
62+
let source = DataSource::new();
63+
64+
// 测试获取有效索引的数据
65+
assert_eq!(source.fetch_data(0).await, Some("Hello".to_string()));
66+
assert_eq!(source.fetch_data(1).await, Some("World".to_string()));
67+
68+
// 测试获取无效索引的数据
69+
assert_eq!(source.fetch_data(10).await, None);
70+
}
71+
72+
#[tokio::test]
73+
async fn test_fetch_all() {
74+
let source = DataSource::new();
75+
let all_data = source.fetch_all().await;
76+
77+
assert_eq!(all_data, vec![
78+
"Hello".to_string(),
79+
"World".to_string(),
80+
"Rust".to_string(),
81+
]);
82+
}
83+
84+
#[tokio::test]
85+
async fn test_count() {
86+
let source = DataSource::new();
87+
assert_eq!(source.count().await, 3);
88+
}
89+
90+
#[tokio::test]
91+
async fn test_concurrent_fetch() {
92+
let source = DataSource::new();
93+
94+
// 测试并发获取数据
95+
let (data1, data2) = tokio::join!(
96+
source.fetch_data(0),
97+
source.fetch_data(1)
98+
);
99+
100+
assert_eq!(data1, Some("Hello".to_string()));
101+
assert_eq!(data2, Some("World".to_string()));
102+
}
103+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Rust 新特性练习
2+
3+
这个章节包含了一些 Rust 最新特性的练习。通过这些练习,你将学习到:
4+
5+
1. const 泛型(Rust 1.51+)
6+
2. GAT (Generic Associated Types) (Rust 1.65+)
7+
3. let-else 语句(Rust 1.65+)
8+
4. 异步特征(Rust 1.75+)
9+
10+
## 推荐阅读
11+
12+
* [Rust 1.51 发布说明](https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html)
13+
* [Rust 1.65 发布说明](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html)
14+
* [Rust 1.75 发布说明](https://blog.rust-lang.org/2023/12/28/Rust-1.75.0.html)
15+
* [Rust Reference: Generic Associated Types](https://doc.rust-lang.org/reference/items/associated-items.html#generic-associated-types-gats)
16+
* [Rust Reference: const 泛型](https://doc.rust-lang.org/reference/items/generics.html#const-generics)

0 commit comments

Comments
 (0)