|
```
pub fn plus_one_test() {
println!("{}","-----vec 修改------------------");
let mut v1 = vec![1, 3, 2];
plus_one(&mut v1);
for (i,val) in v1.iter().enumerate() {
println!("{}:{}",i,val);
}
}
pub fn plus_one(v: &mut Vec
|
|
- for (i,val) in v1.iter().enumerate()
- v1.iter() 返回的迭代器直接产生值的引用 &i32
- val 的类型是 &i32,所以 println! 会自动解引用
- 数组的情况 (g_vec.rs:141):
- for (index,&bb) in aa.iter().enumerate()
- aa.iter() 返回的迭代器也产生值的引用 &i32
- 使用 &bb 模式匹配,将 &i32 解构为 i32,所以 bb 的类型是 i32
- 为什么数组可以用 &bb:
- &bb 是一个模式匹配,用来解引用迭代器产生的引用
- 这样 bb 直接是 i32 类型,而不是 &i32
- 两种方式都可以,但推荐保持一致:
- 要么都用 for (_, val) (保持引用)
- 要么都用 for (_, &val) (解引用)
|
|
对于 Rust 的 i32 类型,i32 和 &i32 的主要区别在于:
1. 所有权和内存
i32 - 拥有值
let x: i32 = 42; // x 拥有值 42,在栈上
&i32 - 引用值
let x: i32 = 42;
let y: &i32 = & x; // y 是指向 x 的引用,不拥有值
```
2. 大小
assert_eq!(std::mem::size_of::< i32 > (), 4); // 4 字节
assert_eq!(std::mem::size_of::< &i32 > (), 8); // 8 字节 (64位系统上)
3. 使用方式
let x: i32 = 42;
let y: &i32 = & x;
// 直接使用 i32
println!("{}", x); // 输出: 42
println!("{}", x + 1); // 可以直接运算
// 使用 &i32 需要解引用
println!("{}", *y); // 输出: 42 (需要 *)
println!("{}", *y + 1); // 需要先解引用
4. 在循环中的应用
回到你之前的代码例子:
// Vec 迭代 - val 是 &i32
for (i, val) in v1.iter().enumerate() {
println!("{}:{}", i, val); // println! 自动解引用
println!("{}:{}", i, *val); // 手动解引用
// println!("{}:{}", i, val + 1); // 错误!不能直接对引用运算
}
// 数组迭代 - &bb 解构后 bb 是 i32
for (index, &bb) in aa.iter().enumerate() {
println!("{}:{}", index, bb); // bb 已经是 i32
println!("{}:{}", index, bb + 1); // 可以直接运算
}
5. 函数参数
fn process_val(val: i32) {
println!("处理值: {}", val);
}
fn process_ref(val: &i32) {
println!("处理引用: {}", *val);
}
let x = 42;
process_val(x); // 传递值
process_ref(&x); // 传递引用
```
总结:i32 是实际的值,而 &i32 是指向值的引用。选择使用哪种取决于是否需要转移所有权。
|
|
这几种方法都是正确的
```
pub fn test1(){
let aa = [1,2,3,4,5];
let mut index = 0;
for bb in &aa {
println!("{}",bb); //引用类型
}
}
pub fn test2(){
let aa = [1,2,3,4,5];
let mut index = 0;
for &bb in &aa {
println!("{}",bb); //解引用
}
}
pub fn test3(){
let aa = [1,2,3,4,5];
let mut index = 0;
for bb in aa.iter() { //迭代器,高效
println!("{}:{}",index,bb);
index = index +1;
}
}
```
|
|
```
pub fn test_for(){
let v1 = vec![1,2,3,4,5,6];
for val in &v1 {
println!("{}",val);
}
let mut v2 = Vec::new();
v2.push(11);
v2.push(12);
for val in &mut v2 {
println!("{}",val);
}
let mut v2 = Vec::new();
v2.push(11);
v2.push(12);
for val in &mut v2 {
*val += 1;
}
println!("{:?}",v2); //[12, 13]
}
```
- 可变引用不可参与相加,数组也是如此
- 确切的说,是要先解引用
```
pub fn test1(){
let aa = [1,2,3,4,5];
let mut index = 0;
for bb in &aa {
println!("{}",bb+1);//对
}
}
pub fn test11(){
let mut aa = [1,2,3,4,5];
let mut index = 0;
for bb in &mut aa {
println!("{}",bb+1); //错
}
}
```
|
|
```
pub fn m11(){
let mut scores = HashMap::new();
scores.insert(String::from("Blue"),10);
scores.insert(String::from("Yellow"),50);
let team_name = String::from("Blue");
let score = scores.get(&team_name);
println!("{:?}",score);
println!("--------------------------");
for (key, value) in &scores {
let val = value + 1;
println!("{}: {}", key, val);
}
}
```
|
|
|
|
```
#![allow(unused)]
fn largest < T: PartialOrd + Copy> (list: &[T]) ->T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
pub fn test() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("The largest char is {}", result);
}
```
|
|
**Rust `println!` 常见格式化输出:**
| 格式 | 说明 | 示例 | 输出 |
|------|------|------|------|
| `{}` | Display 格式,通用展示 | `println!("{}", 42)` | `42` |
| `{:?}` | Debug 格式,调试输出 | `println!("{:?}", v2)` | `[12, 13]` |
| `{:#?}` | Debug 格式,美化缩进 | `println!("{:#?}", v2)` | 多行缩进 |
| `{:b}` | 二进制 | `println!("{:b}", 10)` | `1010` |
| `{:o}` | 八进制 | `println!("{:o}", 10)` | `12` |
| `{:x}` | 十六进制(小写) | `println!("{:x}", 255)` | `ff` |
| `{:X}` | 十六进制(大写) | `println!("{:X}", 255)` | `FF` |
| `{:e}` | 科学计数法(小写e) | `println!("{:e}", 1000.0)` | `1e3` |
| `{:E}` | 科学计数法(大写E) | `println!("{:E}", 1000.0)` | `1E3` |
| `{:p}` | 指针地址 | `println!("{:p}", &v2)` | `0x...` |
| `{:08}` | 宽度8,不足补零 | `println!("{:08}", 42)` | `00000042` |
| `{:<10}` | 左对齐,宽度10 | `println!("{:<10}", "hi")` | `hi ` |
| `{:>10}` | 右对齐,宽度10 | `println!("{:>10}", "hi")` | ` hi` |
| `{:^10}` | 居中对齐,宽度10 | `println!("{:^10}", "hi")` | ` hi ` |
| `{:.2}` | 精度控制(保留2位小数) | `println!("{:.2}", 3.14159)` | `3.14` |
|
|
|
|
|
|
|
|
## as_str
```
pub fn test1(){
//"abc"是 &str 切片类型
let s1 = "abc";
let s2 = s1;
//如果切片具体owner,则下面的语句必报错; 实际上下面的语句是可以正常运行的。
println!("{}",s1);
println!("{}",s2);
}
pub fn test2(){
let s1 = String::from("abcd");;
let s2 = s1;
//由于s1的owner已经转移给s2,下面再使用s1则会报错
// println!("{}",s1);
println!("{}",s2);
}
pub fn test3(){
let s1 = String::from("abcd");
//取s1的切片则没有关系
let s2 = s1.as_str();
println!("{}",s1);
println!("{}",s2);
}
```
|
|
- let fil: String= "/tmp/hello.txt".to_string();
- into_string(),多了一个in,in是进入的意思,
- let B = A.into_string();
- 就是A进到B里去了
- 在rust中就是ownership的转换,
```
首先, A得具有ownership,像切片这种类型本身是另外一个类型的片段数据,没有ownership,就没法用into_string这个方法
切片的传值、传参,只能是将自己指向的地址传递过去,让另外一个变量指向自己指向的地址,所有切片传参一定是这样的 aaa(&name),有一个&
```
```
let B = A.into_string();
调用之后,A就不能再用了,以后生效的只有B
```
它们的方法定义
```
to_string:
fn to_string(&self) -> String;
into_string:
fn into_string(self) -> String;
```
详细
|
|
identity_cols列表要重复很多次的to_string方法
```
impl Default for FeatureConfig {
fn default() -> Self {
Self {
time_col: "DT_TIME".to_string(),
price_col: "AMT".to_string(),
balance_col: "ACCBAL".to_string(),
amount_col: "CNY_AMT".to_string(),
identity_cols: vec![
"ACCT_NUM".to_string(),
"PARTY_ID".to_string(),
"OPP_PARTY_ID".to_string(),
"ACCT".to_string(),
"TCAC".to_string(),
"TSTM".to_string(),
],
}
}
}
```
使用 map 和 collect
```
identity_cols: vec!["ACCT_NUM", "PARTY_ID", "OPP_PARTY_ID", "ACCT", "TCAC", "TSTM"]
.iter()
.map(|s| s.to_string())
.collect(),
```
定义辅助函数
```
fn str_vec_to_string(strings: &[&str]) -> Vec<String> {
strings.iter().map(|s| s.to_string()).collect()
}
// 使用
identity_cols: str_vec_to_string(&["ACCT_NUM", "PARTY_ID", "OPP_PARTY_ID", "ACCT", "TCAC", "TSTM"]),
```
使用宏(更灵活)
```
macro_rules! str_vec {
($($s:expr),*) => {
vec![$($s.to_string()),*]
};
}
// 使用
identity_cols: str_vec!["ACCT_NUM", "PARTY_ID", "OPP_PARTY_ID", "ACCT", "TCAC", "TSTM"],
```
如果使用 const 或 static ``` const IDENTITY_COLS: &[&str] = &["ACCT_NUM", "PARTY_ID", "OPP_PARTY_ID", "ACCT", "TCAC", "TSTM"]; // 使用时 identity_cols: IDENTITY_COLS.iter().map(|s| s.to_string()).collect(), ``` - 推荐使用方法1(map + collect),因为它最简洁且不需要额外的定义。宏方式虽然最简洁,但可能影响代码可读性。 |
|
|
|
|
```
fn add_one_v1 (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
```
参数默认为同一类型
```
pub fn test3() {
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
let n = example_closure(5);
}
```
```
--> src/base/k_closure.rs:53:29
|
53 | let n = example_closure(5);
| ^
| |
| expected struct `String`, found integer
| help: try using a conversion method: `5.to_string()`
```
The first time we call example_closure with the String value,
the compiler infers the type of x and the return type of the closure to be String.
Those types are then locked into the closure in example_closure,
and we get a type error if we try to use a different type with the same closure.
不换数据类型,下面的是正确的
```
pub fn test3() {
let example_closure = |x| x;
let s = example_closure(6);
println!("{}",s);
let n = example_closure(5);
println!("{}",n);
}
```
|
|
Capturing the Environment with Closures
```
fn main() {
let x = 4;
let equal_to_x = |z| z == x;
let y = 4;
assert!(equal_to_x(y));
}
```
Here, even though x is not one of the parameters of equal_to_x,
the equal_to_x closure is allowed to use the x variable
that’s defined in the same scope that equal_to_x is defined in.
Closures can capture values from their environment in three ways,
which directly map to the three ways a function can take a parameter:
taking ownership, borrowing mutably, and borrowing immutably.
These are encoded in the three Fn traits as follows:
FnOnce consumes the variables it captures from its enclosing scope, known as the closure’s environment.
To consume the captured variables,
the closure must take ownership of these variables and move them into the closure when it is defined.
The Once part of the name represents the fact that the closure can’t take ownership of the same variables more than once,
so it can be called only once.
FnMut can change the environment because it mutably borrows values.
Fn borrows values from the environment immutably.
|
|
Closures默认是引用或只使用一次,不具有ownership,因此后面还可以用x
```
pub fn test1() {
let x = 4;
let equal_to_x = |z| z == x;
let y = 4;
assert!(equal_to_x(y));
println!("{}", x); //4
}
```
move:让Closures获取所有相关变量的ownership,后续不可再用 If you want to force the closure to take ownership of the values it uses in the environment, you can use the move keyword before the parameter list. This technique is mostly useful when passing a closure to a new thread to move the data so it’s owned by the new thread. Note: move closures may still implement Fn or FnMut, even though they capture variables by move. This is because the traits implemented by a closure type are determined by what the closure does with captured values, not how it captures them. The move keyword only specifies the latter.
|
|
Rust 闭包捕获外部变量的三种方式(对应 `FnOnce`、`FnMut`、`Fn` trait)详解如下:
---
## 1. 不可变借用(`Fn`)
- **对应操作**:闭包**只读取**外部变量,不修改也不消耗所有权。
- **Trait 约束**:`Fn`(可多次调用,环境不变)。
- **编译器行为**:自动以 `&T` 形式捕获变量。
### 示例
```rust
fn main() {
let x = 42;
let print_x = || println!("{}", x); // 不可变借用 x
print_x();
print_x(); // 可以多次调用
println!("{}", x); // 仍可使用 x
}
```
---
## 2. 可变借用(`FnMut`)
- **对应操作**:闭包**修改**外部变量(如改变其值)。
- **Trait 约束**:`FnMut`(可多次调用,但环境可变)。
- **编译器行为**:以 `&mut T` 形式捕获变量。
### 示例
```rust
fn main() {
let mut count = 0;
let mut increment = || { // 必须声明为 mut
count += 1; // 可变借用 count
println!("{}", count);
};
increment(); // 1
increment(); // 2
// println!("{}", count); // ❌ 此处无法使用,因为闭包仍持有可变借用
// 但闭包调用结束后可变借用释放,后续可用(如在 drop 之后)
}
```
### 示例:drop 闭包后恢复变量使用权
```rust
fn main() {
let mut count = 0;
let mut increment = || {
count += 1;
println!("{}", count);
};
increment(); // 1
increment(); // 2
// println!("{}", count); // ❌ 编译失败:闭包仍持有 count 的可变借用
drop(increment); // 显式 drop 闭包,释放对 count 的可变借用
println!("{}", count); // ✅ 2 — 现在可以正常访问 count
count = 100; // ✅ 也可以重新赋值
println!("{}", count); // ✅ 100
}
```
---
## 3. 获取所有权(`FnOnce`)
- **对应操作**:闭包**消耗**外部变量(如移动 vector、未实现 `Copy` 的类型)。
- **Trait 约束**:`FnOnce`(只能调用一次)。
- **编译器行为**:使用 `move` 关键字或自动推导(当变量未实现 `Copy` 且被消耗时)。
### 示例 1:自动推导
```rust
fn main() {
let v = vec![1, 2, 3];
let consume = || {
let _ = v.into_iter().next(); // 消耗 v 的所有权(into_iter 需要所有权)
};
consume();
// println!("{:?}", v); // ❌ v 的所有权已被移动进闭包
}
```
### 示例 2:使用 `move` 强制转移所有权
```rust
fn main() {
let x = 4;
let equal_to_x = move |z| z == x; // x 被移动进闭包
let y = 4;
assert!(equal_to_x(y));
// println!("{}", x); // ❌ x 不再可用
}
```
---
## 对比总结表
| 方式 | 对应 Trait | 外部变量访问权限 | 可调用次数 | 典型场景 |
|--------------|-------------|---------------------------|------------|-------------------------------|
| 不可变借用 | `Fn` | 只读 | 任意多次 | 纯计算、过滤、日志 |
| 可变借用 | `FnMut` | 读写(修改) | 任意多次 | 计数器、状态累积、迭代器适配器 |
| 获取所有权 | `FnOnce` | 转移所有权(消耗) | 仅一次 | 线程移动数据、异步任务、迭代器 `collect` |
---
## 实际代码中的自动推导规则
Rust 编译器会根据闭包内部的操作自动选择最宽松的捕获方式:
- 只读访问 → `Fn`
- 有 `&mut` 操作 → `FnMut`
- 有所有权转移操作(如 `move`、`into_iter()`、返回值) → `FnOnce`
### 示例:一个闭包可能实现多个 trait
```rust
fn call_once
|
|
|
|
## impl 块的本质
`impl` 是 **implementation** 的缩写,是一个**代码块**,用于为类型定义方法。
### 基本语法
```rust
// 定义结构体
struct MyStruct {
field: i32,
}
// impl 块 - 为类型定义方法
impl MyStruct {
// 关联函数(无 self,类似静态方法)
fn new() -> Self {
MyStruct { field: 0 }
}
// 实例方法(有 self)
fn get_value(&self) -> i32 {
self.field
}
// 可变方法
fn set_value(&mut self, val: i32) {
self.field = val;
}
}
```
### 关联函数 vs 实例方法
| 特性 | 关联函数 | 实例方法 |
|------|----------------------|---------------------|
| **self 参数** | 无 | 有(self/&self/&mut self) |
| **调用方式** | `Type::function()` | `instance.method()` |
| **访问数据** | 不能访问实例数据 | 可以访问实例数据 |
| **典型用途** | 构造函数、工具函数 | 操作实例数据 |
```rust
impl MyStruct {
// 关联函数 - 没有 self 参数
fn create_default() -> Self {
Self { field: 42 }
}
// 实例方法 - 第一个参数是 &self
fn calculate(&self) -> i32 {
self.field * 2
}
}
// 调用方式
let instance = MyStruct::create_default(); // 关联函数
let result = instance.calculate(); // 实例方法
```
### 多个 impl 块
Rust 允许为同一个类型定义多个 `impl` 块:
```rust
impl MyStruct {
fn method1(&self) { }
}
impl MyStruct {
fn method2(&self) { }
}
```
这在实现 trait 时特别有用,可以将 trait 实现与固有方法分开。
### 实际示例
```rust
// 关联函数(工具函数)示例
fn create_missing_column(col_name: &str, height: usize) -> PolarsResult
|
|
## self vs Self
### self(小写)- 实例引用
| 形式 | 所有权 | 使用场景 |
|------|--------|----------|
| `self` | 获取所有权 | 消耗实例,通常用于转换 |
| `&self` | 不可变借用 | 读取数据,不修改 |
| `&mut self` | 可变借用 | 修改数据 |
```rust
impl MyStruct {
// 消耗所有权
fn into_inner(self) -> i32 {
self.field // self 被消耗,调用后实例不再可用
}
// 不可变借用
fn get(&self) -> i32 {
self.field // 只读
}
// 可变借用
fn set(&mut self, val: i32) {
self.field = val; // 修改
}
}
```
### Self(大写)- 类型别名
`Self` 是 Rust 关键字,表示当前类型的类型名:
```rust
impl MyStruct {
fn new() -> Self { // Self 等价于 MyStruct
Self { field: 0 } // Self 等价于 MyStruct
}
fn with_field(mut self, val: i32) -> Self {
self.field = val;
self // 返回 Self 类型
}
}
```
### 在 impl 块内调用关联函数
```rust
impl DataFrameProcessor {
// 关联函数
fn create_column(name: &str) -> Column {
// 实现
}
fn process(&self) -> Result<()> {
// 两种调用方式等价:
let col1 = Self::create_column("col1"); // 推荐
let col2 = DataFrameProcessor::create_column("col2"); // 也可以
Ok(())
}
}
```
### Self 的优势
| 优势 | 说明 |
|------|------|
| **类型安全** | 重命名类型时不需要修改 impl 块内代码 |
| **代码简洁** | 在泛型类型中特别有用 |
| **一致性** | 在 impl 块内统一使用风格更好 |
### 对比总结
```
1. self (小写):
- 表示实例本身
- 只能在方法(有 self 参数的函数)中使用
- 有几种形式:self, &self, &mut self
2. Self (大写):
- 是 Rust 关键字
- 表示当前类型的类型名
- 相当于 impl 块中定义的结构体名称
```
|
|
## impl Trait for Type - 实现 Trait
这是 `impl` 的第二种用法,为类型实现 trait。
### 基本语法
```rust
// 定义 trait
trait Printable {
fn print(&self);
}
// 为类型实现 trait
impl Printable for MyStruct {
fn print(&self) {
println!("Value: {}", self.field);
}
}
```
### 完整示例
```rust
// 定义 trait
trait Animal {
fn speak(&self) -> String;
}
// 为 Dog 实现 Animal trait
struct Dog {
name: String,
}
impl Animal for Dog {
fn speak(&self) -> String {
format!("{} says: Woof!", self.name)
}
}
// 为 Cat 实现 Animal trait
struct Cat {
name: String,
}
impl Animal for Cat {
fn speak(&self) -> String {
format!("{} says: Meow!", self.name)
}
}
fn main() {
let dog = Dog { name: String::from("Buddy") };
let cat = Cat { name: String::from("Whiskers") };
println!("{}", dog.speak()); // Buddy says: Woof!
println!("{}", cat.speak()); // Whiskers says: Meow!
}
```
### trait 默认实现
```rust
trait Animal {
fn speak(&self) -> String;
// 默认实现
fn run(&self) -> String {
format!("{} is running", self.speak())
}
}
impl Animal for Dog {
fn speak(&self) -> String {
String::from("Woof")
}
// run() 使用默认实现,不需要重写
}
```
### impl 的两种用法对比
| 写法 | 用途 | 示例 |
|------|------|------|
| `impl Type` | 定义类型的固有方法 | `impl MyStruct { fn method() }` |
| `impl Trait for Type` | 为类型实现 trait | `impl Display for MyStruct { ... }` |
|
|
## derive 属性 - 自动实现 Trait
### 基本用法
```rust
#[derive(Debug, Clone)]
pub struct FeatureConfig {
time_col: String,
price_col: String,
}
```
`#[derive(Debug, Clone)]` 为结构体自动实现两个 trait:
- **Debug**: 允许使用 `{:?}` 格式化打印
- **Clone**: 提供 `clone()` 方法,创建深拷贝
### 常用 derive Trait
| Trait | 说明 | 使用场景 |
|-------|------|----------|
| `Debug` | 调试输出 | `println!("{:?}", struct)` |
| `Clone` | 深拷贝 | `instance.clone()` |
| `Copy` | 浅拷贝(值类型) | 赋值时自动复制 |
| `PartialEq` | 部分相等比较 | 支持 `==` 操作符 |
| `Eq` | 完全相等比较 | 需要先实现 `PartialEq` |
| `PartialOrd` | 部分排序 | 支持 `<`, `>`, `<=`, `>=` |
| `Ord` | 完全排序 | 需要先实现 `PartialOrd` 和 `Eq` |
| `Default` | 默认值 | `Type::default()` |
| `Hash` | 计算哈希值 | 用于 `HashMap`/`HashSet` |
### Serde 相关(需添加依赖)
```toml
# Cargo.toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
```
```rust
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct User {
id: u32,
name: String,
}
```
### 组合使用示例
```rust
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct MyStruct {
field1: i32,
field2: String,
}
```
### 需要手动实现的 Trait
某些 trait 无法通过 `derive` 自动实现,需要手动编写:
```rust
// 无法 derive,需要手动实现
impl Display for MyStruct {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "MyStruct({})", self.field)
}
}
```
> **trait** 英/treɪt/美/treɪt/ - n. 特质;(人的个性的)特征,特性,特点
|
|
|
|
|
|
|
|
Traits: Defining Shared Behavior
- 注意:struct这个关键字的位置替换为了trait关键字
```
#![allow(unused)]
pub trait Person {
fn food(&self) -> String;
fn eat(&self) -> String {
format!("(eat {}...)", self.food())
}
}
pub struct Teacher {
pub name: String,
}
impl Person for Teacher {
fn food(&self) -> String {
format!("{}", "面包")
}
}
pub struct Student {
pub username: String,
pub age: i8,
}
impl Person for Student {
fn food(&self) -> String {
format!("{}", "水果")
}
}
pub fn test(){
let tch = Teacher {
name: String::from("a01"),
};
println!("teacher: {}", tch.eat());
let st = Student {
username: String::from("Penguins win the Stanley Cup Championship!"),
age: 12,
};
println!("student: {}", st.eat());
}
```
|
|
|
|
|
|
|
|
|
- rust中[1,2]为数组,let v1 = vec![1,2,3,4,5,6];为链表,
- 其中vec!这个!是表示宏的意思吗?宏是指由外部非rust的函数吗
在 Rust 中,你的理解基本正确,但有一个关键概念需要澄清:
## `vec!` 中的 `!` 确实表示宏
`vec!` 是一个**声明宏(declarative macro)**,而不是外部非 Rust 的函数。具体来说:
### 宏的本质
- **宏是 Rust 本身的特性**,不是外部函数
- 宏在**编译时**展开,生成 Rust 代码
- 宏可以接收**不定数量**的参数,这是普通函数做不到的
### `vec!` 宏的作用
```rust
let v1 = vec![1, 2, 3, 4, 5, 6];
```
这个宏会在编译时展开成类似这样的代码:
```rust
let v1 = {
let mut temp = Vec::new();
temp.push(1);
temp.push(2);
temp.push(3);
temp.push(4);
temp.push(5);
temp.push(6);
temp
};
```
### 一个简化说明
- **外部非 Rust 的函数** → 比如 C 语言的 `malloc`,通过 FFI 调用
- **Rust 宏** → 纯 Rust 特性,代码生成器,编译时展开
### 小纠正
你提到的 `[1,2]` 确实是数组,但注意:
- `[1, 2]` 是数组字面量,类型如 `[i32; 2]`
- `vec![1, 2, 3]` 是**动态数组(Vec)**,不是链表
- Rust 中的链表是 `std::collections::LinkedList`
所以 `vec!` 创建的是**可动态增长的数组**(存储在堆上),而不是链表。
那 `[1, 2]` 是在栈上吗?**不一定,取决于上下文:**
- 如果写成 `let arr = [1, 2];`,`[i32; 2]` 是固定大小数组,**直接存储在栈上**。大小在编译期确定,不需要堆分配。
- 如果写成 `let arr: Box<[i32]> = Box::new([1, 2]);`,则数据会被**移动到堆上**,栈上只保留指针。
- 如果作为 `vec![1, 2]` 的输入,`[1, 2]` 先在栈上构造,然后 `vec!` 宏将其中的元素拷贝到**堆上分配的缓冲区**中。
**简单总结:** `[1, 2]` 本身是固定大小的数组类型 `[i32; 2]`,默认在栈上;`vec![1, 2]` 是 `Vec
|
|
- 可以自定义宏吗,比如定义一个参数个数不定的函数,请举例说明
当然可以!Rust 中的宏非常强大,完全可以自定义。下面给你几个实际例子:
## 1. 最简单的自定义宏:计算任意个数的和
```rust
// 定义一个可接受任意数量参数的宏
macro_rules! sum {
// 递归基:单个参数直接返回
($a:expr) => ($a);
// 递归规则:第一个参数 + 剩余参数的和
($a:expr, $($rest:expr),*) => ($a + sum!($($rest),*));
}
fn main() {
let result1 = sum!(1);
let result2 = sum!(1, 2, 3);
let result3 = sum!(10, 20, 30, 40, 50);
println!("sum!(1) = {}", result1); // 输出: 1
println!("sum!(1,2,3) = {}", result2); // 输出: 6
println!("sum!(10,20,30,40,50) = {}", result3); // 输出: 150
}
```
## 2. 创建自定义的 Vec:my_vec!
```rust
// 模仿标准库的 vec! 宏
macro_rules! my_vec {
// 空数组
() => (
Vec::new()
);
// 单个重复值:例如 my_vec![5; 10] 创建10个5
($elem:expr; $n:expr) => (
std::vec::from_elem($elem, $n)
);
// 多个值:例如 my_vec![1, 2, 3]
($($x:expr),+ $(,)?) => ({
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
});
}
fn main() {
let v1 = my_vec![1, 2, 3, 4];
let v2 = my_vec![5; 3]; // 创建 [5, 5, 5]
let v3 = my_vec![]; // 空 Vec
println!("{:?}", v1); // [1, 2, 3, 4]
println!("{:?}", v2); // [5, 5, 5]
println!("{:?}", v3); // []
}
```
## 3. 带标签的打印宏(调试利器)
```rust
// 可变参数的打印宏
macro_rules! debug_print {
// 无参数
() => {
println!();
};
// 单个值
($x:expr) => {
println!("{} = {:?}", stringify!($x), $x);
};
// 多个值
($($x:expr),+ $(,)?) => {
$(
println!("{} = {:?}", stringify!($x), $x);
)*
};
}
fn main() {
let name = "Alice";
let age = 30;
let score = 95.5;
debug_print!(name);
debug_print!(age, score);
// 输出:
// name = "Alice"
// age = 30
// score = 95.5
}
```
## 4. 实用的 HashMap 快速初始化宏
```rust
use std::collections::HashMap;
macro_rules! hash_map {
// 空 HashMap
() => {
HashMap::new()
};
// 键值对:hash_map!{"key1" => value1, "key2" => value2}
($($key:expr => $value:expr),* $(,)?) => {
{
let mut map = HashMap::new();
$(
map.insert($key, $value);
)*
map
}
};
}
fn main() {
let map = hash_map!{
"apple" => 5,
"banana" => 3,
"orange" => 7,
};
println!("{:?}", map); // {"apple": 5, "banana": 3, "orange": 7}
let empty: HashMap<&str, i32> = hash_map!();
println!("Empty: {:?}", empty);
}
```
## 关键语法说明
- **`$var:expr`** - 匹配一个表达式
- **`$($rest:expr),*`** - 匹配0个或多个表达式,逗号分隔
- **`$($rest:expr),+`** - 匹配1个或多个表达式
- **`stringify!($x)`** - 将表达式转为字符串
- **`$(...)*`** - 重复内部模式0次或多次
- **`$(...),+`** - 重复内部模式1次或多次,并插入逗号
## 宏 vs 函数的区别
```rust
// 函数不能实现不定参数(除非用切片)
fn sum_fixed(nums: &[i32]) -> i32 {
nums.iter().sum()
}
// 但宏可以做到像普通函数调用一样简洁
macro_rules! sum_flex {
($($x:expr),*) => {
[$($x),*].iter().sum::
|
|
|
|
|
|
|
|
|
|
|
|
|