diff --git a/Cargo.toml b/Cargo.toml index 27531b37bf..72d85ad476 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/exercises/24_new_features/01_const_generics.rs b/exercises/24_new_features/01_const_generics.rs new file mode 100644 index 0000000000..d05f798688 --- /dev/null +++ b/exercises/24_new_features/01_const_generics.rs @@ -0,0 +1,67 @@ +// const_generics.rs +// +// Const 泛型允许我们使用编译时常量作为泛型参数。这在处理固定大小的数组和其他 +// 需要编译时常量的场景特别有用。 +// +// 在这个练习中,我们将实现一个简单的固定大小数组包装器,它可以安全地访问数组元素 +// 并提供一些实用的方法。 + +#![allow(dead_code)] + +// TODO: 实现一个泛型结构体 FixedArray +// 它应该包装一个固定大小的数组 [T; N] +struct FixedArray { + data: [T; N], +} + +impl FixedArray { + // 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 = FixedArray::new([]); + assert_eq!(arr.len(), 0); + assert_eq!(arr.get(0), None); + } +} \ No newline at end of file diff --git a/exercises/24_new_features/02_let_else.rs b/exercises/24_new_features/02_let_else.rs new file mode 100644 index 0000000000..d044580892 --- /dev/null +++ b/exercises/24_new_features/02_let_else.rs @@ -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 { + todo!("实现 Rectangle::new,使用 let-else 语句验证参数") + } + + // TODO: 使用 let-else 语句实现这个函数 + // 函数应该解析字符串格式的矩形定义,格式为 "x1,y1,x2,y2" + // 其中 x1,y1 是左上角坐标,x2,y2 是右下角坐标 + fn parse(s: &str) -> Option { + 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()); + } +} \ No newline at end of file diff --git a/exercises/24_new_features/03_gat.rs b/exercises/24_new_features/03_gat.rs new file mode 100644 index 0000000000..b506e0a3bd --- /dev/null +++ b/exercises/24_new_features/03_gat.rs @@ -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); + +// TODO: 为 Wrapper 实现 Container 特征 +// Value 类型应该是对 T 的引用 +impl Container for Wrapper { + type Value<'a> = todo!("定义正确的关联类型"); + + fn get<'a>(&'a self) -> Self::Value<'a> { + todo!("返回对内部值的引用") + } +} + +// 一个选项包装类型 +struct OptionWrapper(Option); + +// TODO: 为 OptionWrapper 实现 Container 特征 +// Value 类型应该是 Option<&T> +impl Container for OptionWrapper { + 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 = 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"); + } +} \ No newline at end of file diff --git a/exercises/24_new_features/04_async_trait.rs b/exercises/24_new_features/04_async_trait.rs new file mode 100644 index 0000000000..50ceca5fc7 --- /dev/null +++ b/exercises/24_new_features/04_async_trait.rs @@ -0,0 +1,103 @@ +// async_trait.rs +// +// 异步特征是 Rust 1.75 中稳定的新特性。它允许在特征中直接定义异步方法, +// 不再需要使用 async-trait 宏。这个特性大大简化了异步编程的代码。 +// +// 在这个练习中,我们将实现一个简单的异步数据获取接口。 + +#![allow(dead_code)] + +use std::time::Duration; + +// 模拟一个数据源 +struct DataSource { + data: Vec, +} + +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; + async fn fetch_all(&self) -> Vec; + async fn count(&self) -> usize; +} + +// TODO: 为 DataSource 实现 AsyncDataFetcher 特征 +impl AsyncDataFetcher for DataSource { + async fn fetch_data(&self, index: usize) -> Option { + todo!("模拟异步获取数据,使用 tokio::time::sleep 增加延迟") + } + + async fn fetch_all(&self) -> Vec { + 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())); + } +} \ No newline at end of file diff --git a/exercises/24_new_features/README.md b/exercises/24_new_features/README.md new file mode 100644 index 0000000000..bc6ff9bc86 --- /dev/null +++ b/exercises/24_new_features/README.md @@ -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) \ No newline at end of file