爬虫 做资讯网站,cms内容网站管理系统,深圳企业建站公司,网站建设费的摊销期目录 一、概念二、Box\T\2.1 概念与应用场景2.2 简单应用2.3 递归类型的创建 三、通过Deref trait将智能指针当作常规引用处理3.1 常规引用3.2 像引用一样使用Box\T\3.3 自定义智能指针3.4 函数和方法的隐式解引用强制转换3.5 解引用强制转换与可变性交互 四、… 目录 一、概念二、Box\T\2.1 概念与应用场景2.2 简单应用2.3 递归类型的创建 三、通过Deref trait将智能指针当作常规引用处理3.1 常规引用3.2 像引用一样使用Box\T\3.3 自定义智能指针3.4 函数和方法的隐式解引用强制转换3.5 解引用强制转换与可变性交互 四、使用Drop Trait清理代码4.1 自动运行4.2 手动丢弃 一、概念
在Rust中引用是只是借用数据的指针智能指针拥有它们所指向的数据的所有权智能指针通常使用结构体实现智能指针实现了Deref trait值可以被当作引用对待智能指针实现了Drop trait当值离开作用域时其所指向的堆数据也去被清除常用的智能指针见下表
指针功能说明BoxT用于在堆上分配值允许在编译时执行不可变或可变借用检查RcT一个引用计数类型相同数据可以有多个所有者仅允许在编译时执行不可变借用检查RefCellT允许在运行时执行不可变或可变借用检查可以在即使RefCellT 自身是不可变的情况下修改其内部的值RefT 和 RefMutT通过RefCellT 访问
内部可变性模式在不可变值内部改变值
二、BoxT
2.1 概念与应用场景
box是最简单最直接的智能指针其类型是boxTbox主要应用于以下场景 编译时未知大小的类型但使用时却需要知道它的确切大小有大量数据且希望在确保数据不被拷贝的情况下转移所有权只关心值的类型是否实现了特定 trait
2.2 简单应用
fn main(){let b Box::new(5);println!(b {}, b);
}变量b指向了分配在堆上的值为5的Boxb拥有这块内存的所有权离开作用域后堆内存被自动释放
2.3 递归类型的创建
Rust需要在编译时知道类型占用的空间大小box的已知大小让其可以在循环类型定义中插入box就可以创建递归类型
enum List{Cons(i32, BoxList),Nil,
}use crate::List::{Cons, Nil};
fn main() {let list Cons(1,Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}Cons成员将需要一个i32类型的空间大小以及box指针数据的空间Nil成员不存储值因此它比Cons成员需要更少的空间看起来像这样 如果不用Box定义递归写成下面这样 enum List{Cons(i32, List),Nil,
}use crate::List::{Cons, Nil};
fn main() {let list Cons(1, Cons(2, Cons(3, Nil)));
}则编译报错表明类型占用的空间无限大 其空间排布类型于 三、通过Deref trait将智能指针当作常规引用处理
实现Deref trait可以让使用者重载解引用运算符dereference operator *这种方式实现Deref trait的智能指针可以被当作常规引用来对待可以编写操作引用的代码并用于智能指针
3.1 常规引用
常规引用是一种指针类型
fn main() {let x 5;let y x;assert_eq!(5, x);assert_eq!(5, *y);
}y等于x的引用使用*y访问x的值
3.2 像引用一样使用BoxT let x 5;let y Box::new(x);assert_eq!(5, x);assert_eq!(5, *y);代码可正常运行不报错
3.3 自定义智能指针
use std::ops::Deref;struct MyBoxT(T);implT MyBoxT {fn new(x: T) - MyBoxT {MyBox(x)}
}implT Deref for MyBoxT{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);
}MyBoxT 被定义为包含一个元素的元组结构体new函数获取一个T类型的参数并返回一个存入传入值的实例为MyBox实现Deref trait才能启动*运算符的解引用功能implT Deref for MyBoxT中的type Target T 定义了此trait的关联类型deref方法返回了一个值的引用如果直接返回值则值的选择权将被移出self当使用*y时底层运行了代码*(y.deref())
3.4 函数和方法的隐式解引用强制转换
解引用强制转换只能工作在实现了Dereftrait 的类型上解引用强制转换是将一种类型隐式转换为另外一种类型的引用前一种类型实现了Dereftrait并且其关联类型是后一种类型
例如解引用强制转换可以将 String 转换为 str因为类型 String 实现了 Deref trait 并且其关联类型是 str
#[stable(feature rust1, since 1.0.0)]
impl ops::Deref for String {type Target str;#[inline]fn deref(self) - str {unsafe { str::from_utf8_unchecked(self.vec) }}
}将特定类型的值的引用传递给函数且与函数定义的参数类型不匹配时会发生解引用强制转换此时有一系列的deref方法被调用将我们提供的参数类型转换成函数或方法需要的参数类型解引用强制转换功能可以让开发者编写函数和方法调用时无需增加过多显式使用和*引用和解引用。解引用强制转换功能也使得开发者可以编写更多同时作用于引用或智能指针的代码
use std::ops::Deref;struct MyBoxT(T);implT MyBoxT {fn new(x: T) - MyBoxT {MyBox(x)}
}implT Deref for MyBoxT{type Target T;fn deref(self) - T {self.0}
}fn hello(name: str){println!(Hello, {}, name);
}fn main() {let m MyBox::new(String::from(Rust));hello(m);
}main函数中的m为MyBoxString值的引用MyBoxT上实现了DereftraitRust可以通过deref调用将MyBoxString变为String再次调用deref将String 变为 str如果没有实现解引用强制转换为了使用MyBoxString类型的值调用hello函数应该这样写
fn main() {let m MyBox::new(String::from(Rust));hello((*m)[..]);
}(*m) 将 MyBoxString 解引用为 String和[…] 获取了整个 String 的字符串 slice 来匹配 hello 函数的参数没有解引用强制转换所有这些符号混在一起将更难以读写和理解Rust的解引用强制转换发生在编译因此在运行时没有损耗
3.5 解引用强制转换与可变性交互
类似于使用 Deref trait 重载不可变引用的*运算符Rust提供了DerefMut trait用于重载可变引用的*运算符;Rust 在发现类型和 trait 的实现满足以下三种情况时会进行解引用强制转换; 当 T: DerefTargetU 从 T 到 U 如果有一个T而T实现了返回U类型的Deref则可以直接得到U当 T: DerefMutTargetU 从 mut T 到 mut U 对于可变引用有着与第一种相同的行为当 T: DerefTargetU 从 mut T 到 U Rust也会将可变引用强转为不可变引用但是反之是不可能的因为不可变引用永远也不能强转为可变引用
四、使用Drop Trait清理代码
4.1 自动运行
通过实现Droptrait指定变量离开作用域时被执行的代码可以理解为析构函数
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(stuff c) };}let d CustomSmartPointer { data: String::from(stuff d) };let e CustomSmartPointer { data: String::from(stuff e) };println!(CustomSmartPointers created.);
}main函数中离开最内层的大括号后变量c首先离开作用域自动调用drop方法然后打印CustomSmartPointers created.变量d、e最后离开作用域再自动调用对应的drop方法d、e的输出结果显示以先进后出的方式调用drop方法 4.2 手动丢弃
不能显式的调用drop方法如果要在作用域结构之前强制释放变量使用drop(x)实现
fn main() {{let c CustomSmartPointer { data: String::from(stuff c) };}let d CustomSmartPointer { data: String::from(stuff d) };drop(d);let e CustomSmartPointer { data: String::from(stuff e) };println!(CustomSmartPointers created.);
}运行代码可以发现d被提前析构