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."
This commit is contained in:
Shenwenkun 2025-05-20 10:36:36 +08:00
parent e36dd7a120
commit 9db703b468
6 changed files with 341 additions and 0 deletions

View File

@ -54,6 +54,7 @@ rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" }
serde_json = "1.0"
serde.workspace = true
toml_edit.workspace = true
tokio = { version = "1.36", features = ["full"] }
[target.'cfg(not(windows))'.dependencies]
rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] }

View File

@ -0,0 +1,67 @@
// const_generics.rs
//
// Const 泛型允许我们使用编译时常量作为泛型参数。这在处理固定大小的数组和其他
// 需要编译时常量的场景特别有用。
//
// 在这个练习中,我们将实现一个简单的固定大小数组包装器,它可以安全地访问数组元素
// 并提供一些实用的方法。
#![allow(dead_code)]
// TODO: 实现一个泛型结构体 FixedArray<T, const N: usize>
// 它应该包装一个固定大小的数组 [T; N]
struct FixedArray<T, const N: usize> {
data: [T; N],
}
impl<T, const N: usize> FixedArray<T, N> {
// TODO: 实现 new 方法,它接受一个数组并返回 FixedArray
fn new(arr: [T; N]) -> Self {
todo!("创建一个新的 FixedArray 实例")
}
// TODO: 实现 get 方法,它安全地返回索引处的元素引用
fn get(&self, index: usize) -> Option<&T> {
todo!("返回指定索引处的元素,如果索引越界则返回 None")
}
// TODO: 实现 len 方法,返回数组的长度
fn len(&self) -> usize {
todo!("返回数组的长度")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fixed_array() {
let arr = FixedArray::new([1, 2, 3, 4, 5]);
// 测试长度
assert_eq!(arr.len(), 5);
// 测试有效索引
assert_eq!(arr.get(0), Some(&1));
assert_eq!(arr.get(4), Some(&5));
// 测试无效索引
assert_eq!(arr.get(5), None);
}
#[test]
fn test_different_types() {
let arr = FixedArray::new(["hello", "world"]);
assert_eq!(arr.len(), 2);
assert_eq!(arr.get(0), Some(&"hello"));
assert_eq!(arr.get(1), Some(&"world"));
}
#[test]
fn test_empty_array() {
let arr: FixedArray<i32, 0> = FixedArray::new([]);
assert_eq!(arr.len(), 0);
assert_eq!(arr.get(0), None);
}
}

View File

@ -0,0 +1,77 @@
// let_else.rs
//
// let-else 语句是 Rust 1.65 中引入的新特性。它允许我们在模式匹配失败时
// 提前返回或中断执行。这个特性特别适合于处理 Option 和 Result 类型。
//
// 在这个练习中,我们将使用 let-else 语句来简化错误处理代码。
#[derive(Debug, PartialEq)]
struct Point {
x: i32,
y: i32,
}
#[derive(Debug, PartialEq)]
struct Rectangle {
top_left: Point,
bottom_right: Point,
}
impl Rectangle {
// TODO: 使用 let-else 语句实现这个函数
// 如果参数无效(左上角点的坐标大于右下角点的坐标),返回 None
fn new(top_left: Point, bottom_right: Point) -> Option<Rectangle> {
todo!("实现 Rectangle::new使用 let-else 语句验证参数")
}
// TODO: 使用 let-else 语句实现这个函数
// 函数应该解析字符串格式的矩形定义,格式为 "x1,y1,x2,y2"
// 其中 x1,y1 是左上角坐标x2,y2 是右下角坐标
fn parse(s: &str) -> Option<Rectangle> {
todo!("实现字符串解析为 Rectangle 的功能")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_rectangle() {
let rect = Rectangle::new(
Point { x: 0, y: 0 },
Point { x: 10, y: 10 }
);
assert!(rect.is_some());
}
#[test]
fn test_invalid_rectangle() {
let rect = Rectangle::new(
Point { x: 10, y: 10 },
Point { x: 0, y: 0 }
);
assert!(rect.is_none());
}
#[test]
fn test_parse_valid() {
let rect = Rectangle::parse("0,0,10,10");
assert_eq!(rect, Some(Rectangle {
top_left: Point { x: 0, y: 0 },
bottom_right: Point { x: 10, y: 10 }
}));
}
#[test]
fn test_parse_invalid_format() {
assert!(Rectangle::parse("invalid").is_none());
assert!(Rectangle::parse("0,0,10").is_none());
assert!(Rectangle::parse("0,0,10,10,12").is_none());
}
#[test]
fn test_parse_invalid_coordinates() {
assert!(Rectangle::parse("10,10,0,0").is_none());
}
}

View File

@ -0,0 +1,77 @@
// gat.rs
//
// GAT (Generic Associated Types) 是 Rust 1.65 中引入的一个强大特性。
// 它允许在关联类型中使用泛型参数,这在创建容器类型和迭代器时特别有用。
//
// 在这个练习中,我们将实现一个简单的 Map 容器,它可以存储不同类型的值
// 并提供类型安全的访问方法。
#![allow(dead_code)]
// 定义一个特征,表示可以存储和检索值的容器
trait Container {
// TODO: 使用 GAT 定义一个关联类型 Value它有一个生命周期参数
type Value<'a>: 'a
where
Self: 'a;
// 获取容器中的值的引用
fn get<'a>(&'a self) -> Self::Value<'a>;
}
// 一个简单的包装类型
struct Wrapper<T>(T);
// TODO: 为 Wrapper<T> 实现 Container 特征
// Value 类型应该是对 T 的引用
impl<T> Container for Wrapper<T> {
type Value<'a> = todo!("定义正确的关联类型");
fn get<'a>(&'a self) -> Self::Value<'a> {
todo!("返回对内部值的引用")
}
}
// 一个选项包装类型
struct OptionWrapper<T>(Option<T>);
// TODO: 为 OptionWrapper<T> 实现 Container 特征
// Value 类型应该是 Option<&T>
impl<T> Container for OptionWrapper<T> {
type Value<'a> = todo!("定义正确的关联类型");
fn get<'a>(&'a self) -> Self::Value<'a> {
todo!("返回 Option<&T>")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wrapper() {
let w = Wrapper(42);
assert_eq!(*w.get(), 42);
}
#[test]
fn test_option_wrapper_some() {
let w = OptionWrapper(Some(42));
assert_eq!(w.get(), Some(&42));
}
#[test]
fn test_option_wrapper_none() {
let w: OptionWrapper<i32> = OptionWrapper(None);
assert_eq!(w.get(), None);
}
// 这个测试确保我们的实现可以处理不同的生命周期
#[test]
fn test_lifetime() {
let w = Wrapper(String::from("hello"));
let r: &String = w.get();
assert_eq!(r, "hello");
}
}

View File

@ -0,0 +1,103 @@
// async_trait.rs
//
// 异步特征是 Rust 1.75 中稳定的新特性。它允许在特征中直接定义异步方法,
// 不再需要使用 async-trait 宏。这个特性大大简化了异步编程的代码。
//
// 在这个练习中,我们将实现一个简单的异步数据获取接口。
#![allow(dead_code)]
use std::time::Duration;
// 模拟一个数据源
struct DataSource {
data: Vec<String>,
}
impl DataSource {
fn new() -> Self {
Self {
data: vec![
"Hello".to_string(),
"World".to_string(),
"Rust".to_string(),
],
}
}
}
// TODO: 实现一个异步特征 AsyncDataFetcher
// 它应该包含以下异步方法:
// - fetch_data: 获取指定索引的数据
// - fetch_all: 获取所有数据
// - count: 获取数据总数
trait AsyncDataFetcher {
async fn fetch_data(&self, index: usize) -> Option<String>;
async fn fetch_all(&self) -> Vec<String>;
async fn count(&self) -> usize;
}
// TODO: 为 DataSource 实现 AsyncDataFetcher 特征
impl AsyncDataFetcher for DataSource {
async fn fetch_data(&self, index: usize) -> Option<String> {
todo!("模拟异步获取数据,使用 tokio::time::sleep 增加延迟")
}
async fn fetch_all(&self) -> Vec<String> {
todo!("模拟异步获取所有数据")
}
async fn count(&self) -> usize {
todo!("模拟异步获取数据数量")
}
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::time::sleep;
#[tokio::test]
async fn test_fetch_data() {
let source = DataSource::new();
// 测试获取有效索引的数据
assert_eq!(source.fetch_data(0).await, Some("Hello".to_string()));
assert_eq!(source.fetch_data(1).await, Some("World".to_string()));
// 测试获取无效索引的数据
assert_eq!(source.fetch_data(10).await, None);
}
#[tokio::test]
async fn test_fetch_all() {
let source = DataSource::new();
let all_data = source.fetch_all().await;
assert_eq!(all_data, vec![
"Hello".to_string(),
"World".to_string(),
"Rust".to_string(),
]);
}
#[tokio::test]
async fn test_count() {
let source = DataSource::new();
assert_eq!(source.count().await, 3);
}
#[tokio::test]
async fn test_concurrent_fetch() {
let source = DataSource::new();
// 测试并发获取数据
let (data1, data2) = tokio::join!(
source.fetch_data(0),
source.fetch_data(1)
);
assert_eq!(data1, Some("Hello".to_string()));
assert_eq!(data2, Some("World".to_string()));
}
}

View File

@ -0,0 +1,16 @@
# Rust 新特性练习
这个章节包含了一些 Rust 最新特性的练习。通过这些练习,你将学习到:
1. const 泛型Rust 1.51+
2. GAT (Generic Associated Types) (Rust 1.65+)
3. let-else 语句Rust 1.65+
4. 异步特征Rust 1.75+
## 推荐阅读
* [Rust 1.51 发布说明](https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html)
* [Rust 1.65 发布说明](https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html)
* [Rust 1.75 发布说明](https://blog.rust-lang.org/2023/12/28/Rust-1.75.0.html)
* [Rust Reference: Generic Associated Types](https://doc.rust-lang.org/reference/items/associated-items.html#generic-associated-types-gats)
* [Rust Reference: const 泛型](https://doc.rust-lang.org/reference/items/generics.html#const-generics)