智能指针
智能指针(序)
相关的概念
- 指针:一个变量在内存中包含的是一个地址(指向其它数据)
- Rust 中最常见的指针就是”引用“
- 引用:
- 使用 &
- 借用它指向的值
- 没有其余开销
- 最常见的指针类型
智能指针
- 智能指针是这样一些数据结构:
- 行为和指针相似
- 有额外的元数据和功能
引用计数(Reference counting)智能指针类型
- 通过记录所有者的数量,使一份数据被多个所有者同时持有
- 并在没有任何所有者时自动清理数据
引用和智能指针的其它不同
- 引用:只借用数据
- 智能指针:很多时候都拥有它所指向的数据
智能指针的例子
-
String 和
Vec<T>
-
都拥有一片内存区域,且允许用户对其操作
-
还拥有元数据(例如容量等)
-
提供额外的功能或保障(String 保障其数据是合法的 UTF-8 编码)
智能指针的实现
- 智能指针通常使用 Struct 实现,并且实现了:
- Deref 和 Drop 这两个 trait
- Deref trait:允许智能指针 struct 的实例像引用一样使用
- Drop trait:允许你自定义当智能指针实例走出作用域时的代码
本章内容
- 介绍标准库中常见的智能指针
Box<T>
:在 heap 内存上分配值Rc<T>
:启用多重所有权的引用计数类型Ref<T>
和RefMut<T>
,通过RefCell<T>
访问:在运行时而不是编译时强制借用规则的类型
- 此外:
- 内部可变模型(interior mutability pattern):不可变类型暴露出可修改其内部值的 API
- 引用循环(reference cycles):它们如何泄露内存,以及如何防止其发生。
一、使用Box<T>
来指向 Heap 上的数据
Box<T>
Box<T>
是最简单的智能指针:- 允许你在 heap 上存储数据(而不是 stack)
- stack 上是指向 heap 数据的指针
- 没有性能开销
- 没有其它额外功能
- 实现了 Deref trait 和 Drop trait
Box<T>
的常用场景
- 在编译时,某类型的大小无法确定。但使用该类型时,上下文却需要知道它的确切大小。
- 当你有大量数据,想移交所有权,但需要确保在操作时数据不会被复制。
- 使用某个值时,你只关心它是否实现了特定的 trait,而不关心它的具体类型。
使用Box<T>
在heap上存储数据
fn main() {
let b = Box::new(5);
println!("b = {}", b);
} // b 释放存在 stack 上的指针 heap上的数据
使用 Box 赋能递归类型
- 在编译时,Rust需要知道一个类型所占的空间大小
- 而递归类型的大小无法再编译时确定
- 但 Box 类型的大小确定
- 在递归类型中使用 Box 就可解决上述问题
- 函数式语言中的 Cons List
关于 Cons List
- Cons List 是来自 Lisp 语言的一种数据结构
- Cons List 里每个成员由两个元素组成
- 当前项的值
- 下一个元素
- Cons List 里最后一个成员只包含一个 Nil 值,没有下一个元素 (Nil 终止标记)
Cons List 并不是 Rust 的常用集合
- 通常情况下,Vec
是更好的选择 - (例子)创建一个 Cons List
use crate::List::{Cons, Nil};
fn main() {
let list = Cons(1, Cons(2, Cons(3, Nil)));
}
enum List { // 报错
Cons(i32, List),
Nil,
}
- (例)Rust 如何确定为枚举分配的空间大小
enum Message {
Quit,
Move {x: i32, y: i32},
Write(String),
ChangeColor(i32, i32, i32),
}
使用 Box 来获得确定大小的递归类型
- Box
是一个指针,Rust知道它需要多少空间,因为: - 指针的大小不会基于它指向的数据的大小变化而变化
use crate::List::{Cons, Nil};
fn main() {
let list = Cons(1,
Box::new(Cons(2,
Box::new(Cons(3,
Box::new(Nil))))));
}
enum List {
Cons(i32, Box<List>),
Nil,
}
- Box
: - 只提供了”间接“存储和 heap 内存分配的功能
- 没有其它额外功能
- 没有性能开销
- 适用于需要”间接“存储的场景,例如 Cons List
- 实现了 Deref trait 和 Drop trait
二、Deref Trait(1)
Deref Trait
- 实现 Deref Trait 使我们可以自定义解引用运算符 * 的行为。
- 通过实现 Deref,智能指针可像常规引用一样来处理
解引用运算符
- 常规引用是一种指针
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
把 Box<T>
当作引用
Box<T>
可以替代上例中的引用
fn main() {
let x = 5;
let y = Box::new(x);
assert_eq!(5, x);
assert_eq!(5, *y);
}
定义自己的智能指针
Box<T>
被定义成拥有一个元素的 tuple struct- (例子)
MyBox<T>
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
fn main() {
let x = 5;
let y = MyBox::new(x); // 报错
assert_eq!(5, x);
assert_eq!(5, *y);
}
实现 Deref Trait
- 标准库中的 Deref trait 要求我们实现一个 deref 方法:
- 该方法借用 self
- 返回一个指向内部数据的引用
- (例子)
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y); // *(y.deref())
}
三、Deref Trait (2)
函数和方法的隐式解引用转化(Deref Coercion)
- 隐式解引用转化(Deref Coercion)是为函数和方法提供的一种便捷特性
- 假设 T 实现了 Deref trait:
- Deref Coercion 可以把 T 的引用转化为 T 经过 Deref 操作后生成的引用
- 当把某类型的引用传递给函数或方法时,但它的类型与定义的参数类型不匹配:
- Deref Coercion 就会自动发生
- 编译器会对 deref 进行一系列调用,来把它转为所需的参数类型
- 在编译时完成,没有额外性能开销
use std::ops::Deref;
fn hello(name: &str) {
println!("Hello, {}", name);
}
fn main() {
let m = MyBox::new(String::from("Rust"));
// &m &MyBox<String> 实现了 deref trait
// deref &String
// deref &str
hello(&m);
hello(&(*m)[..]);
hello("Rust");
}
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
assert_eq!(5, *y); // *(y.deref())
}
解引用与可变性
- 可使用 DerefMut trait 重载可变引用的 * 运算符
- 在类型和 trait 在下列三种情况发生时,Rust会执行 deref coercion:
- 当 T:Deref<Target=U>,允许 &T 转换为 &U
- 当 T:DerefMut<Target=U>,允许 &mut T 转换为 &mut U
- 当 T:Deref<Target=U>,允许 &mut T 转换为 &U
四、Drop Trait
Drop Trait
- 实现 Drop Trait,可以让我们自定义当值将要离开作用域时发生的动作。
- 例如:文件、网络资源释放等
- 任何类型都可以实现 Drop trait
- Drop trait 只要求你实现 drop 方法
- 参数:对self 的可变引用
- Drop trait 在预导入模块里(prelude)
/*
* @Author: QiaoPengjun5162 [email protected]
* @Date: 2023-04-13 21:39:51
* @LastEditors: QiaoPengjun5162 [email protected]
* @LastEditTime: 2023-04-13 21:46:50
* @FilePath: /smart/src/main.rs
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
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.")
}
运行
smart on master [?] is
标签:Cons,编程语言,let,fn,Rc,RefCell,new,Rust,指针
From: https://www.cnblogs.com/QiaoPengjun/p/17324022.html