Rust语法学习
这里将学习Rust中感到困惑的一些Rust语法进行整理
变量隐藏
1 |
|
这段代码的输出结果是
1 | in for : 0 |
在 C++ 中,当你在 for
循环内部声明一个新的变量 x
并试图初始化它为 x * 2
时,这里的 x
实际上是指向新声明的 x
,而不是外部作用域的 x
。因为新声明的 x
在这个时候还没有被初始化,所以 x * 2
的结果是未定义的,但在大多数情况下,它会被初始化为 0。
这是因为在 C++ 中,新的变量 x
的声明和初始化是在同一条语句中完成的,所以在 x
的值被计算(即 x * 2
)时,新的 x
已经遮蔽了外部作用域的 x
。
1 | fn main() { |
而rust中的结果是
1 | inner 12 |
这段代码中的 x
变量在不同的作用域中有不同的值。在 Rust 中,可以在一个作用域中重新声明一个与外部作用域同名的变量,这被称为变量遮蔽(shadowing)。
首先,x
被初始化为 5。然后,x
被重新声明并赋值为 x + 1
,所以 x
的值变为 6。
然后,进入一个新的作用域(由 {}
定义)。在这个作用域中,x
被重新声明并赋值为 x * 2
,所以在这个作用域中,x
的值变为 12。这个值在 println!("inner {x}");
语句中被打印出来。
当离开这个作用域时,我们回到了外部作用域,x
的值再次变为 6。这个值在 println!("outter {x}");
语句中被打印出来。
字符串与所有权
结构体元组
成员相同但名称不同的元组不是同一种元组,不能相互赋值
生命周期的3个规则
生命周期约束
'a : 'b
表示 a >= b
子类型的生命周期
Sub
是 Super
的子类型, Sub
的生命周期要包含 Super
的范围,有可能更大
目前Rust生命周期的子类型关系对于泛型存在三种映射
- 如果
T
是U
的一个子类型意味着F<T>
是F<U>
的一个子类型(即子类型化“通过(passes through)”),则F<T>
在T
上是协变的(covariant)_。 - 如果
U
是T
的一个子类型意味着F<U>
是F<T>
的一个子类型,则F<T>
在T
上是_逆变的(contravariant)_。 - 其他情况下(即不能由参数类型的子类型化关系推导出此泛型的型变关系),
F<T>
在T
上是的_不变的(invariant)_。
引用的生命周期
引用的生命周期从借用处开始,一直到最后一次使用的地方
再引用(ReBorrow
), 将指针解引用后再引用的行为,如let ptr2 : Point = &*ptr1;
const泛型
无界生命周期
闭包
闭包中如果未声明参数类型那么一定要使用,否则编译器无法判断是什么类型
闭包根据参数在函数体内如何使用判断捕获参数的类型,可变还是不可变
使用move
关键字强制获取所有权
智能指针
表现类似指针,同时拥有额外的元数据和功能,如String
在内存中的分布
Box<T>
- 当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候
- 当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候
- 当希望拥有一个值并只关心它的类型是否实现了特定trit而不是其具体类型的时候
使用
Box<T>
创建递归类型
示例
1 | enum List { |
实现Deref
以及函数和方法的隐式转换
通过std::mem::drop
提早丢弃值
Rc<T>
引用计数
Rc::new
创建
Rc::clone(&a)
,获取只读所有权
调用Rc::strong_count
获取计数
RefCell<T>
和内部可变性
对于引用Box<T>
借用规则不可变性作用于编译时,对于RefCell<T>
不可变性作用于运行时,如果违反则panic
。
原子计数引用Arc<T>
多线程中的Rc<T>