上海哪个公司做网站好/2022年seo还值得做吗
智能指针
Box
Box 的使用场景
由于 Box 是简单的封装,除了将值存储在堆上外,并没有其它性能上的损耗。而性能和功能往往是鱼和熊掌,因此 Box 相比其它智能指针,功能较为单一,可以在以下场景中使用它:
- 特意的将数据分配在堆上
- 数据较大时,又不想在转移所有权时进行数据拷贝
- 类型的大小在编译期无法确定,但是我们又需要固定大小的类型时
- 特征对象,用于说明对象实现了一个特征,而不是某个特定的类型
Box 堆对象分配
因为 Box 允许你将一个值分配到堆上,然后在栈上保留一个智能指针指向堆上的数据。
当栈上数据转移所有权时,实际上是把数据拷贝了一份,最终新旧变量各自拥有不同的数据,因此所有权并未转移。
fn main() {let a = Box::new(3);println!("a = {}", a); // a = 3// 下面一行代码将报错// let b = a + 1; // cannot add `{integer}` to `Box<{integer}>`//正确//let b = *a + 1
}
}
将动态大小类型变为 Sized 固定大小类型
Rust 需要在编译时知道类型占用多少空间,如果一种类型在编译时无法知道具体的大小,那么被称为动态大小类型 DST。
其中一种无法在编译时知道大小的类型是递归类型:在类型定义中又使用到了自身,或者说该类型的值的一部分可以是相同类型的其它值,这种值的嵌套理论上可以无限进行下去,所以 Rust 不知道递归类型需要多少空间:
enum List {Cons(i32, List),Nil,
}
以上就是函数式语言中常见的 Cons List,它的每个节点包含一个 i32 值,还包含了一个新的 List,因此这种嵌套可以无限进行下去,Rust 认为该类型是一个 DST 类型,并给予报错:
error[E0072]: recursive type `List` has infinite size //递归类型 `List` 拥有无限长的大小--> src/main.rs:3:1|
3 | enum List {| ^^^^^^^^^ recursive type has infinite size
4 | Cons(i32, List),| ---- recursive without indirection
此时若想解决这个问题,就可以使用我们的 Box:
enum List {Cons(i32, Box<List>),Nil,
}
只需要将 List 存储到堆上,然后使用一个智能指针指向它,即可完成从 DST 到 Sized 类型(固定大小类型)的华丽转变。
Box 中还提供了一个非常有用的关联函数:Box::leak,它可以消费掉 Box 并且强制目标值从内存中泄漏,读者可能会觉得,这有啥用啊?
fn main() {let s = gen_static_str();println!("{}", s);
}fn gen_static_str() -> &'static str{let mut s = String::new();s.push_str("hello, world");Box::leak(s.into_boxed_str())
}
特征对象
在 Rust 中,想实现不同类型组成的数组只有两个办法:枚举和特征对象,前者限制较多,因此后者往往是最常用的解决办法。
trait Draw {fn draw(&self);
}struct Button {id: u32,
}
impl Draw for Button {fn draw(&self) {println!("这是屏幕上第{}号按钮", self.id)}
}struct Select {id: u32,
}impl Draw for Select {fn draw(&self) {println!("这个选择框贼难用{}", self.id)}
}fn main() {let elems: Vec<Box<dyn Draw>> = vec![Box::new(Button { id: 1 }), Box::new(Select { id: 2 })];for e in elems {e.draw()}
}
Box::leak
Box 中还提供了一个非常有用的关联函数:Box::leak,它可以消费掉 Box 并且强制目标值从内存中泄漏,读者可能会觉得,这有啥用啊?
其实还真有点用,例如,你可以把一个 String 类型,变成一个 'static 生命周期的 &str 类型:
fn main() {let s = gen_static_str();println!("{}", s);
}fn gen_static_str() -> &'static str{let mut s = String::new();s.push_str("hello, world");Box::leak(s.into_boxed_str())
}
Deref 解引用
智能指针的名称来源,主要就在于它实现了 Deref 和 Drop 特征,这两个特征可以智能地帮助我们节省使用上的负担:
Deref 可以让智能指针像引用那样工作,这样你就可以写出同时支持智能指针和引用的代码,例如 *T
Drop 允许你指定智能指针超出作用域后自动执行的代码
常规引用的解引用。
常规引用是一个指针类型,包含了目标数据存储的内存地址。对常规引用使用 * 操作符,就可以通过解引用的方式获取到内存地址对应的数据值:
fn main() {let x = 5;let y = &x;assert_eq!(5, x);assert_eq!(5, *y);
}
这里 y 就是一个常规引用,包含了值 5 所在的内存地址,然后通过解引用 *y,我们获取到了值 5。如果你试图执行 assert_eq!(5, y);,代码就会无情报错,因为你无法将一个引用与一个数值做比较:
error[E0277]: can't compare `{integer}` with `&{integer}` //无法将{integer} 与&{integer}进行比较--> src/main.rs:6:5|
6 | assert_eq!(5, y);| ^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &{integer}`|= help: the trait `PartialEq<&{integer}>` is not implemented for `{integer}`// 你需要为{integer}实现用于比较的特征PartialEq<&{integer}>
智能指针解引用
实现 Deref 后的智能指针结构体,就可以像普通引用一样,通过 * 进行解引用,例如 Box 智能指针:
fn main() {let x = Box::new(1);let sum = *x + 1;
}
智能指针 x 被 * 解引用为 i32 类型的值 1,然后再进行求和。
定义自己的智能指针
由于 Box 本身很简单,并没有包含类如长度、最大长度等信息,因此用一个元组结构体即可。
struct MyBox<T>(T);impl<T> MyBox<T> {fn new(x: T) -> MyBox<T> {MyBox(x)}
}
跟 Box 一样,我们的智能指针也持有一个 T 类型的值,然后使用关联函数 MyBox::new 来创建智能指针。由于还未实现 Deref 特征,此时使用 * 肯定会报错:
fn main() {let y = MyBox::new(5);assert_eq!(5, *y);
}
运行后,报错如下:
error[E0614]: type `MyBox<{integer}>` cannot be dereferenced--> src/main.rs:12:19|
12 | assert_eq!(5, *y);| ^^
为智能指针实现 Deref 特征
现在来为 MyBox 实现 Deref 特征,以支持 * 解引用操作符:
use std::ops::Deref;impl<T> Deref for MyBox<T> {type Target = T;fn deref(&self) -> &Self::Target {&self.0}
}
很简单,当解引用 MyBox 智能指针时,返回元组结构体中的元素 &self.0,有几点要注意的:
在 Deref 特征中声明了关联类型 Target,在之前章节中介绍过,关联类型主要是为了提升代码可读性
deref 返回的是一个常规引用,可以被 * 进行解引用
* 背后的原理
当我们对智能指针 Box 进行解引用时,实际上 Rust 为我们调用了以下方法:
*(y.deref())
首先调用 deref 方法返回值的常规引用,然后通过 * 对常规引用进行解引用,最终获取到目标值。
要注意的是,* 不会无限递归替换,从 *y 到 *(y.deref()) 只会发生一次。
函数和方法中的隐式 Deref 转换
对于函数和方法的传参,Rust 提供了一个极其有用的隐式转换:Deref 转换。若一个类型实现了 Deref 特征,那它的引用在传给函数或方法时,会根据参数签名来决定是否进行隐式的 Deref 转换,例如:
fn main() {let s = String::from("hello world");display(&s)
}fn display(s: &str) {println!("{}",s);
}
以上代码有几点值得注意:
String 实现了 Deref 特征,可以在需要时自动被转换为 &str 类型
- &s 是一个 &String 类型,当它被传给 display 函数时,自动通过 Deref 转换成了 &str
- 必须使用 &s 的方式来触发 Deref(仅引用类型的实参才会触发自动解引用)
连续的隐式 Deref 转换
Deref 可以支持连续的隐式转换,直到找到适合的形式为止:
fn main() {let s = MyBox::new(String::from("hello world"));display(&s)
}fn display(s: &str) {println!("{}",s);
}
这里我们使用了之前自定义的智能指针 MyBox,并将其通过连续的隐式转换变成 &str 类型:首先 MyBox 被 Deref 成 String 类型,结果并不能满足 display 函数参数的要求,编译器发现 String 还可以继续 Deref 成 &str,最终成功的匹配了函数参数。
引用归一化
Rust 编译器实际上只能对 &v 形式的引用进行解引用操作,那么问题来了,如果是一个智能指针或者 &&&&v 类型的呢? 该如何对这两个进行解引用?
答案是:Rust 会在解引用时自动把智能指针和 &&&&v 做引用归一化操作,转换成 &v 形式,最终再对 &v 进行解引用:
把智能指针(比如在库中定义的,Box、Rc、Arc、Cow 等)从结构体脱壳为内部的引用类型,也就是转成结构体内部的 &v
把多重&,例如 &&&&&&&v,归一成 &v
三种 Deref 转换
实际上 Rust 还支持将一个可变的引用转换成另一个可变的引用以及将一个可变引用转换成不可变的引用,规则如下:
- 当 T: Deref<Target=U>,可以将 &T 转换成 &U,也就是我们之前看到的例子
- 当 T: DerefMut<Target=U>,可以将 &mut T 转换成 &mut U
- 当 T: Deref<Target=U>,可以将 &mut T 转换成 &U
来看一个关于 DerefMut 的例子:
struct MyBox<T> {v: T,
}impl<T> MyBox<T> {fn new(x: T) -> MyBox<T> {MyBox { v: x }}
}use std::ops::Deref;impl<T> Deref for MyBox<T> {type Target = T;fn deref(&self) -> &Self::Target {&self.v}
}use std::ops::DerefMut;impl<T> DerefMut for MyBox<T> {fn deref_mut(&mut self) -> &mut Self::Target {&mut self.v}
}fn main() {let mut s = MyBox::new(String::from("hello, "));display(&mut s)
}fn display(s: &mut String) {s.push_str("world");println!("{}", s);
}
以上代码有几点值得注意:
Rc 与 Arc
引用计数(reference counting),顾名思义,通过记录一个数据被引用的次数来确定该数据是否正在被使用。当引用次数归零时,就代表该数据不再被使用,因此可以被清理释放。
use std::rc::Rc;
fn main() {
let a = Rc::new(String::from("hello, world"));
let b = Rc::clone(&a);
assert_eq!(2, Rc::strong_count(&a));
assert_eq!(Rc::strong_count(&a), Rc::strong_count(&b))}
以上代码我们使用 Rc::new 创建了一个新的 Rc 智能指针并赋给变量 a,该指针指向底层的字符串数据。智能指针 Rc 在创建时,还会将引用计数加 1,此时获取引用计数的关联函数 Rc::strong_count 返回的值将是 1。Rc::clone接着,我们又使用 Rc::clone 克隆了一份智能指针 Rc,并将该智能指针的引用计数增加到 2。由于 a 和 b 是同一个智能指针的两个副本,因此通过它们两个获取引用计数的结果都是 2。不要被 clone 字样所迷惑,以为所有的 clone 都是深拷贝。这里的 clone 仅仅复制了智能指针并增加了引用计数,并没有克隆底层数据,因此 a 和 b 是共享了底层的字符串 s,这种复制效率是非常高的。
Rc 简单总结
- Rc/Arc 是不可变引用,你无法修改它指向的值,只能进行读取,如果要修改,需要配合内部可变性 RefCell 或互斥锁 Mutex
- 一旦最后一个拥有者消失,则资源会自动被回收,这个生命周期是在编译期就确定下来的
- Rc 只能用于同一线程内部,想要用于线程之间的对象共享,你需要使用 Arc
- Rc 是一个智能指针,实现了 Deref 特征,因此你无需先解开 Rc 指针,再使用里面的 T,而是可以直接使用 T
Arc
Arc 是 Atomic Rc 的缩写,顾名思义:原子化的 Rc 智能指针。它能保证我们的数据能够安全的在线程间共享即可.
Arc 和 Rc 拥有完全一样的 API,修改起来很简单:
use std::sync::Arc;use std::thread;
fn main() {
let s = Arc::new(String::from("多线程漫游者"));
for _ in 0..10 {
let s = Arc::clone(&s);
let handle = thread::spawn(move || { println!("{}", s) }); }}
Cell 和 RefCell
因此 Rust 提供了 Cell 和 RefCell 用于内部可变性,简而言之,可以在拥有不可变引用的同时修改目标数据。
Cell
Cell 和 RefCell 在功能上没有区别,区别在于 Cell 适用于 T 实现 Copy 的情况.
use std::cell::Cell;
fn main() {let c = Cell::new("asdf");let one = c.get();c.set("qwer");let two = c.get();println!("{},{}", one, two);
}
RefCell
由于 Cell 类型针对的是实现了 Copy 特征的值类型,因此在实际开发中,Cell 使用的并不多,因为我们要解决的往往是可变、不可变引用共存导致的问题,此时就需要借助于 RefCell 来达成目的。
所有权、借用规则与这些智能指针做一个对比:
Rust 规则 | 智能指针带来的额外规则 |
---|---|
一个数据只有一个所有者 | Rc/Arc让一个数据可以拥有多个所有者 |
要么多个不可变借用,要么一个可变借用 | RefCell实现编译期可变、不可变引用共存 |
违背规则导致编译错误 | 违背规则导致运行时panic |
use std::cell::RefCell;fn main() {let s = RefCell::new(String::from("hello, world"));let s1 = s.borrow();let s2 = s.borrow_mut();println!("{},{}", s1, s2);
}
Rc + RefCell 组合使用
Rust 中,一个常见的组合就是 Rc 和 RefCell 在一起使用,前者可以实现一个数据拥有多个所有者,后者可以实现数据的可变性:
use std::cell::RefCell;
use std::rc::Rc;
fn main() {let s = Rc::new(RefCell::new("我很善变,还拥有多个主人".to_string()));let s1 = s.clone();let s2 = s.clone();// let mut s2 = s.borrow_mut();s2.borrow_mut().push_str(", oh yeah!");println!("{:?}\n{:?}\n{:?}", s, s1, s2);
}
相关文章:

rust 智能指针
智能指针 Box Box 的使用场景 由于 Box 是简单的封装,除了将值存储在堆上外,并没有其它性能上的损耗。而性能和功能往往是鱼和熊掌,因此 Box 相比其它智能指针,功能较为单一,可以在以下场景中使用它: 特…...

CentOS 7系统安装配置Zabbix 5.0LTS 步骤
目录 一、查看Zabbix官方教程(重点) 二、安装 Docker 创建 Mysql 容器 安装 Docker 依赖包 添加 Docker 官方仓库 安装 Docker 引擎 启动 Docker 服务并设置开机自启 验证 Docker 是否成功安装 拉取 MySQL 镜像 查看本地镜像 运行容器 停止和启…...

【学习之路】Multi Agent Reinforcement Learning框架与代码
【学习之路】Multi Agent Reiforcement Learning框架与代码 Introduction 国庆期间,有个客户找我写个代码,是强化学习相关的,但我没学过,心里那是一个慌,不过好在经过详细的调研以及自身的实力,最后还是解…...

android 13.0 SystemUI导航栏添加虚拟按键功能(二)
1.概述 在13.0的系统产品开发中,对于在SystemUI的原生系统中默认只有三键导航,想添加其他虚拟按键就需要先在构建导航栏的相关布局 中分析结构,然后添加相关的图标xml就可以了,然后添加对应的点击事件,就可以了,接下来先分析第二步关于导航栏的相关布局情况 然后实现功能…...

Java8 新特性之Stream(二)-- Stream的中间操作
目录 1.filter(Predicate) 2.map(Function) 3.flatMap(Function) 4.distinct() 5.sorted([Comparator]) 6.limit(n) 7.skip(n) 8.peek(Consumer)...

CA与区块链之数字签名详解
CA与区块链验证本质上都是数字签名,首先,我们看一下什么是数字签名! 数字签名 数字签名是公钥密码学中的一种技术,用于验证信息的完整性和发送者的身份。简而言之,数字签名是一种确认信息来源和信息完整性的手段。它通…...

一文解读如何应用 REST 对资源进行访问?
文章目录 一、REST 简介二、涉及注解2.1 RequestMapping2.2 PathVariable2.3 RestController2.4 GetMapping、PostMapping、PutMapping、DeleteMapping补充:PathVariable、RequestBody、RequestParam 区别与应用 三、REST风格案例 一、REST 简介 REST (Representat…...

使用JAVA发送邮件
这里用java代码编写发送邮件我采用jar包,需要先点击这里下载三个jar包:这三个包分别为:additionnal.jar;activation.jar;mail.jar。这三个包缺一不可,如果少添加或未添加均会报下面这个错误: C…...

【JavaEE】_servlet程序的编写方法
目录 1. 创建项目 2. 引入依赖 3. 创建目录结构 3.1 在main目录下创建一个webapp目录 3.2 在webapp目录下创建一个WEB-INF目录 3.3 在WEB-INF目录下创建一个web.xml文件 3.4 在web.xml中进行代码编写 4. 编写代码 4.1 在java目录下创建类 4.2 打印"hello world&…...

美国市场三星手机超苹果 中国第一属华为
报告显示,截至5月份的三个月,iOS系统在美国、澳大利亚以及日本表现不俗。Android系统份额则在英国、德国以及法国实现增长。在中国城市地区,iOS份额同比基本持平,而Android份额则达到80.5%,同比增长1个百分点。 三星在…...

nodejs+vue+elementui医院挂号预约管理系统4n9w0
前端技术:nodejsvueelementui 前端:HTML5,CSS3、JavaScript、VUE 1、 node_modules文件夹(有npn install Express 框架于Node运行环境的Web框架, 开发语言 node.js 框架:Express 前端:Vue.js 数据库:mysql 数据库工具ÿ…...

调试技巧(课件图解)
...

react中获取input输入框内容的两种方法
一.通过event对象信息的方式 <input onChange{(e)>this.inputChange(e)}/> <button onClick{()>this.getInputValue} >获取input的值</button>inputChange(e){alert(e.target.value)this.setState({username:e.target.value}) } getInputValue(){aler…...

Linux基础—1
1、命令行 1) 重要快捷键 按键作用Tab命令补全Ctrl强行终止当前程序Ctrld键盘输入结束或退出终端Ctrls暂停当前程序,暂停后按下任意键恢复运行Ctrlz将当前程序放到后台运行,恢复到前台为命令fgCtrla将光标移至输入行头,相当于Home键Ctrle将…...
十个面试排序算法
一、 前言 最常考的是快速排序和归并排序,并且经常有面试官要求现场写出这两种排序的代码。对这两种排序的代码一定要信手拈来才行。还有插入排序、冒泡排序、堆排序、基数排序、桶排序等。面试官对于这些排序可能会要求比较各自的优劣、各种算法的思想及其使用场景…...

技术学习群-第四期内容共享
本期是技术群聊的第四期。还是那句话,《群聊免费进入》。一起来看看本期分享内容。 uiautomator-Error问题 在使用u2的过程中,有时候需要使用到uiautomator这个工具来进行查阅层级。但是博主遇到了这么个问题。 《问题分析》:发生此问题的原因…...

冒泡排序/鸡尾酒排序
冒泡排序 冒泡排序(Bubble Sort)是一种简单的排序算法,它通过多次交换相邻元素的位置来实现排序。它的基本思想是从数组的第一个元素开始,比较相邻的两个元素,如果它们的顺序错误,则交换它们的位置。重复进…...

代码随想录算法训练营第五十三天|309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费
代码随想录算法训练营第五十三天|309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费 309.最佳买卖股票时机含冷冻期714.买卖股票的最佳时机含手续费 309.最佳买卖股票时机含冷冻期 题目链接:309.最佳买卖股票时机含冷冻期 文章链接 状态:有…...

【Docker】Docker的使用案例以及未来发展、Docker Hub 服务、环境安全、容器部署安全
作者简介: 辭七七,目前大二,正在学习C/C,Java,Python等 作者主页: 七七的个人主页 文章收录专栏: 七七的闲谈 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖…...

qt qtabwidget获取当前选项卡的所有按键
要获取当前选项卡中的所有按键,可以通过以下步骤进行: 通过currentIndex()函数获取当前选项卡的索引。 使用widget()函数获取当前选项卡的QWidget。 连接QWidget的keyPressEvent事件,并在事件处理函数中获取按下的按键信息。 下面是示例代…...

为什么Excel插入图片不显示,点击能够显示
很久没有Excel了,今天在做Excel表格时,发现上传图片后不能显示,但是点击还是能够出现图片的 点击如下 点击能看到,但是不显示? 最后发现只需鼠标右键点击浮动即可显示...

使用Python创建faker实例生成csv大数据测试文件并导入Hive数仓
文章目录 一、Python生成数据1.1 代码说明1.2 代码参考 二、数据迁移2.1 从本机上传至服务器2.2 检查源数据格式2.3 检查大小并上传至HDFS 三、beeline建表3.1 创建测试表并导入测试数据3.2 建表显示内容 四、csv文件首行列名的处理4.1 创建新的表4.2 将旧表过滤首行插入新表 一…...

qml基础语法
文章目录 基础语法例子 属性例子 核心元素元素item RectangleText例子 Image例子 MouseArea例子Component(组件)例子简单变换例子 定位器ColumnRowGridFlowRepeater 布局InputKeys 基础语法 QML是一种用于描述对象如何相互关联的声明式语言。 QtQuick是…...

数据结构 - 2(顺序表10000字详解)
一:List 1.1 什么是List 在集合框架中,List是一个接口,继承自Collection。 Collection也是一个接口,该接口中规范了后序容器中常用的一些方法,具体如下所示: Iterable也是一个接口,Iterabl…...

05在IDEA中配置Maven的基本信息
配置Maven信息 配置Maven家目录 每次创建Project工程后都需要设置Maven家目录位置,否则IDEA将使用内置的Maven核心程序和使用默认的本地仓库位置 一般我们配置了Maven家目录后IDEA就会自动识别到conf/settings.xml配置文件和配置文件指定的本地仓库位置创建新的P…...

基于IDEA 配置Maven环境和JDK版本(全局)
1.首先启动IDEA,进去初始界面 选择 Customize 之后,选择 All settings 2. 选择下图中的列表配置 3. 找到Maven下的Runner, 配置JRE的版本,选择自己下载使用的jdk的版本即可 4.最后配置Compiler 下的 Java Compiler 选择自己的jdk版本号&am…...

mysql数据库 windows迁移至linux
1.打开navicat,选择一个数据库进行操作: 之后文件会保存为一个xxx.sql文件,之后打开xftp,把生成的sql放进一个文件夹中(/home/dell/linuxmysql): 之后登录mysql数据库,并创建一个新的数据库,然后…...

P4491 [HAOI2018] 染色
传送门:洛谷 解题思路: 写本题需要知道一个前置知识: 假设恰好选 k k k个条件的方案数为 f ( k ) f(k) f(k);先钦定选 k k k个条件,其他条件无所谓的方案数为 g ( k ) g(k) g(k) 那么存在这样的一个关系: g ( k ) ∑ i k n C i k f ( i ) g(k)\sum_{ik}^nC_{i}^kf(i) g(k)…...

12096 - The SetStack Computer (UVA)
题目链接如下: Online Judge 这道题我一开始的思路大方向其实是对的,但细节怎么实现set到int的哈希没能想清楚(没想到这都能用map)。用set<string>的做法来做,测试数据小的话答案是对的,但大数据时…...

Pygame中将鼠标形状设置为图片2-1
在Pygame中利用Sprite类的派生类将鼠标形状设置为图片,其原理就是将Sprite类的派生类对应图片的位置设置为鼠标的当前位置即可。其效果如图1所示。 图1 将鼠标设置为图片 从图1可以看出,鼠标的形状变为红色的,该红色的随着鼠标的移动而移动&…...