指针:是指一个包含了内存地址的变量,这个地址代表或者指向其它的数据,最常用的一种指针就是引用reference,用符号&表示,借用了指向的值
智能指针:一种结构体,不止具备指针的能力,并且包含了额外的元数据metadata和能力,比如引用计数智能指针(追踪数据的多个所有者,如果所有者为0,则数据被销毁);
冷知识:String和Vec
智能指针一般用结构体struct来实现,区别与一般的struct,它还会实现Dref和Drop这两个trait,Dref解引用允许智能指针具有引用的能力,Drop允许自定义销毁变量时候的行为
Box
Box
看代码
fn main() {
let b = Box::new(5);
println!("b = {}", b);
}
println语句执行完后,b会销毁,同时b指向的堆上的数据5也会被销毁
如下代码无法编译通过:
enum List {
Cons(i32, List),
Nil,
}
use crate::List::{Cons, Nil};
fn main() {
let list = Cons(1, Cons(2, Cons(3, Nil)));
}
报错如下:
$ cargo run
Compiling cons-list v0.1.0 (file:///projects/cons-list)
error[E0072]: recursive type `List` has infinite size
--> src/main.rs:1:1
|
1 | enum List {
| ^^^^^^^^^ recursive type has infinite size
2 | Cons(i32, List),
| ---- recursive without indirection
|
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable
|
2 | Cons(i32, Box),
| ^^^^ ^
error[E0391]: cycle detected when computing drop-check constraints for `List`
--> src/main.rs:1:1
|
1 | enum List {
| ^^^^^^^^^
|
= note: ...which again requires computing drop-check constraints for `List`, completing the cycle
= note: cycle used when computing dropck types for `Canonical { max_universe: U0, variables: [], value: ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing }, value: List } }`
Some errors have detailed explanations: E0072, E0391.
For more information about an error, try `rustc --explain E0072`.
error: could not compile `cons-list` due to 2 previous errors
报错信息的意思是infinite size无穷大小,原因是其中含有递归recursive,Rust无法判断它占用的空间大小
Rust如何计算非递归类型所占空间大小
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
枚举类型的大小,Rust会选择占用空间最大的那个
智能指针-实现了Dref trait的引用reference
如下代码编译失败!can't compare {integer}
with &{integer}
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
不可以把两个不同类型一起比较,一个是integer,一个是&integer,不具备可比性
上述代码修改成:
fn main() {
let x = 5;
let y = Box::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
使用Box
自定义智能指针 MyBox
struct MyBox(T);
impl MyBox {
fn new(x: T) -> MyBox {
MyBox(x)
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
此行代码assert_eq!(5, *y) 还是不能通过编译,原因是MyBox
所以,需要新增Dref trait的实现
impl Deref for MyBox {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
*y背后的代码其实是 *(y.deref())
函数或方法中的强制隐式解引用
fn hello(name: &str) {
println!("Hello, {}!", name);
}
fn main() {
let m = MyBox::new(String::from("Rust"));
hello(&m);
}
上面代码中,hello函数的参数是&str,是String的切片,由于MyBox
如果Rust没有解引用胁迫机制,hello函数的调用要写成 hello(&(*m)[..]); 可读性非常差;Rust的解引用根据需要可以执行许多次,为了要找到合适的匹配参数的引用,这个动作是在编译器,所以对程序运行时没有影响
可变性中的强制解引用
- From &T to &U when T: Deref
- From &mut T to &mut U when T: DerefMut
- From &mut T to &U when T: Deref
Drop trait
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("my stuff"),
};
let d = CustomSmartPointer {
data: String::from("other stuff"),
};
println!("CustomSmartPointers created.");
}
可以使用std::mem::drop来提前释放占用的资源
RC引用计数智能指针
大部分场景下,一个变量对一个值拥有所有权,但是某些场景下,如图结构,某个节点是可以有多条边的,除非所有的边都释放了所有权,这个值才能被释放
enum List {
Cons(i32, Rc),
Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("count after creating a = {}", Rc::strong_count(&a));
let b = Cons(3, Rc::clone(&a));
println!("count after creating b = {}", Rc::strong_count(&a));
{
let c = Cons(4, Rc::clone(&a));
println!("count after creating c = {}", Rc::strong_count(&a));
}
println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}
a变量,第一次定义和声明的时候,引用计数为1;b定义的时候,a的引用计数加1,变为2,c定义的时候,a的引用计数又增加1,变为3,当c在{}执行完后,由于c释放,导致a的引用计数减1,变为2,然后当main函数执行完毕后,a的引用变为0,a的资源得到释放
RefCell 内部可变
强制在运行时应用借用规则,应用场景如mock object