初学rust——Iterators、Closures、Crates.io and Pointers
学习rust的第五、六天,学习资料是官网的《The Rust Programming Language》,本笔记的内容包括第13-15章的内容
Chapter 13 Functional Language Features: Iterators and Closure
本章主要内容:
- Closure,function-like的结构,可以存储在变量中
- Iterators,处理一系列元素的方法
- 如何利用以上两种特性来增强上一章的代码
- 以上两种特性的性能分析
13.1 Closures: Anonymous Functions that Can Capture Their Environment
目标:代码在程序中仅定义一次,同时仅在需要时调用一次
closure的声明使用let语句,声明表示这个closure包含一个匿名函数的定义,不代表其包含一个匿名函数的运行结果。
closure不要求声明参数的类型,或返回值的类型,这一点与函数是不同的。由于函数作为显式的用户接口,必须声明其参数与返回值的类型,作为所有使用这个函数的成员的“共识”。但是闭包不同,闭包不作为暴露的接口,仅为一个匿名函数,存储在变量中,因而不需要做类型声明。
closure的类型是编译器推测的,但是仅能用作一种类型。
所有的闭包至少实现以下之一的trait:
Fn, FnMut, FnOnce
闭包具有的函数所没有的性质:capture their environment and access variables from the scope in which they’re defined.
fn main() {let x = 4;let equal_to_x = |z| z == x; //注意此处,我们并没有把x作为参数传递给闭包,但是它依然可以访问x的值,因为他们定一下相同的scope下let y = 4;assert!(equal_to_x(y));
}
closure从环境中抓取值的方式有三种,这三种方式分别对应着函数获得变量的三种方法:taking ownership、borrowing mutably和borrowing immutably。他们被编码进了三种Fn trait:
FnOnce
从其scope中获取变量,并得到变量的ownership,将其move到closure中。Once表示闭包无法超过一次获得同个变量的ownershipFnMut
mutably borrow,从而可以改变环境Fn
immutably borrow
13.2 Processing a Series of Items with Iterators
所有的Iterator都具有trait:Iterator
,在标准库中定义:
pub trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;
// methods with default implementations elided
}
定义一个iterator的关键在于实现next函数
注意rust中的iterator是“lazy”的,什么也不做,除非被调用。
13.3 Improving Our I/O Project
本节内容见代码Chapter12
13.4 Comparing Performance: Loops vs. Iterators
本节内容仅给出结论:Iterator的速度很快,比loop的效率高,可以放心大胆地使用~
zero-cost abstraction
Chapter 14, More About Cargo and Crates.io
本章的内容可以帮助程序媛做到:
- Customize your build through released profile
- 在crates.io发布libraries
- 使用workshop组织大规模的项目
- 从crates.io安装binaries
- 使用custom commands扩展Cargo
14.1 Customizing Builds
Cargo具有两个主要文件:dev
:在运行cargo run时使用;release
:在运行cargo build --release。
当Cargo.toml文件中没有指定[profile.*]
时,cargo对每个文件都有默认的设置。通过加入[profile.*]
可以重写默认值:
[profile.dev]
opt-level = 0 //规定了rust对代码进行优化的次数[profile.release]
opt-level = 3
14.2 Publishing a Crate to Crates.io
(1)使用有效的文档注释,方便其他人快速了解代码
Documentation comment: ///
该文档注释功能能够进行HTML build,命令是cargo doc
,使用这个功能还可以在运行cargo test的时候自动运行注释文档中的代码,作为test~
//!
注释的内容一般作为crates和modules的解释
(2) 利用pub use
导出public API
当代码具有很复杂的结构时,其他人难以使用其中的某个模块,导入也相对复杂。
可以重新导出某个模块,使得其他人可以直接使用,而不必搞清楚整个代码的逻辑架构,一层一层找到这个模块
(3) 设置一个Crates.io Account
在crates.io的主页通过github账号登录获取账户和API token
(4) 向新的Crate加入metadata
在发布crate之前,需要向Cargo.toml文件中加入[packages] section
Crates.io上的crates的命名具有唯一性,不可以与已存在的重复。
description和license也是必须要求的内容,同样写在Cargo.toml中
(5) 发布
注意发布是永久的,已经发布不能删除,可以更新版本。
可以拒绝其他项目将发布的crates作为依赖。cargo yank
命令
14.3 Cargo Workspaces
Workspaces可以帮助我们管理多个相关的packages
Workspace 是一组共用同一个Cargo.lock的packages,因而其中所有的crates会共用所有的依赖,当发生更新时也是同步更新。
14.4 Installing Binaries from Crates.io with cargo install
注意只能安装具有binary targets的packages
14.5 Extending Cargo with Custom Commands
Chapter 15, Smart Pointers
学到这我才发现之前学的reference不完全等价于指针…真是一个尴尬的事情…
pointers,指针,包含内存中的一个地址。这个地址指向另一个数据。Rust中最常见的pointer之一就是之前提到的reference,由符号&
来表示。
smart pointers,是一种数据结构,不仅具有指针的能力,而且还有metadata和其他的能力。smart pointers与reference的主要区别之一就是reference只borrow数据,而smart pointers拥有数据。前文中提到过的String、Vec都是smart pointers的例子。
Smart pointers一般使用结构体来实现,一个区分smart pointers和普通结构体的主要性质就是smart pointers实现Deref
和Drop
traits。其中Deref
允许一个sp的实例拥有reference的功能,Drop
能够帮助程序媛在sp超出scope后去customize代码。
本章会涉及rust中最常见的标准库中的sp:
Box<T>
将数据分配到堆上Rc<T>
reference counting type,enable多个ownershipRef<T>
和RefMut<T>
accessed throughRefCall<T>
,将borrowing规则施加在运行时而不是编译时。
除此之外还会涉及interior mutability和reference cycles
15.1 Using Box<T>
to Point to Data on the Heap
Box是最为直接的sp,写作Box。Boxes允许程序媛在堆上存储数据而非栈,栈中唯一存放的是指向堆的指针。在以下情况中经常用到Boxes:
- 当有一个类型在编译时不能确定大小,但是想要使用这个类型时需要一个明确的大小。
- 当有很多数据想要转换ownership,但是想要保证在这个过程中不会发生copy。
- 当想要own一个值,只关心它实现了特定的trait,而不太关心这个值的具体类型时
fn main() { let b = Box::new(5); //定义println!("b = {}", b);
}
注意在Box out of scope时,他会同时释放栈中存放的指向数据堆的指针,和数据堆。
Enabling Recursive Types with Boxes
在编译时不知道具体大小的类型称为recursive type(递归类型),其中一个值可以作为其自身的一部分拥有同一类型的另一个值,由于在理论上这个递归可以无穷进行下去,所以rust无法确定这个类型的大小。
举一个栗子:cons list。cons list是一个来自Lisp编程语言和衍生的数据结构,在Lisp中,cons function是construct function的简称,从它的两个arguments中构建一个新的pair。两个arguments通常为一个单值和另一个pair(因此构成了递归),pairs构成了一个list。
在cons list中,每一个item包含两个元素:current item和next item。表中的最后一个元素只包含一个值Nil,没有next item。
15.2 Treating Smarts Pointers Like Regular Referencens with the Deref
Trait
符号*
:dereference operator,实现了Deref trait,sp在这种运算符下,与reference等价。
一般理解,*v
操作,是 &v
的反向操作,即试图由资源的引用获取到资源的拷贝(如果资源类型实现了 Copy
),或所有权(资源类型没有实现 Copy
)。
Rust中该操作符可以重载
let x = 5;
let y = &x;assert_eq!(5, x);
assert_eq!(5, *y);
deref最为神奇的设计应属他的“deref coercion
”,强制隐式转换,其规则为:
一个类型为T
的对象foo
,如果 T: Deref<Target=U>
,那么,相关 foo
的某个sp或引用(比如 &foo
)在应用的时候会自动转换成 &U。
考虑以下代码:
fn hello(name: &str) {println!("Hello, {}!", name);
}
fn main() {let m = MyBox::new(String::from("Rust"));hello(&m);
}
这里hello函数的参数是一个&str,主函数调用hello,传参&m,而m是MyBox<String>的引用。如果没有deref coercoin的实现,代码应写为:
fn main() {let m = MyBox::new(String::from("Rust"));hello(&(*m)[..]);
}
*m将MyBox<String>转换为String,然后&和[…]从String中取全切片
可以使用DerefMut
对可变引用重写操作符*
。
在以下三种情况下,rust会进行强制隐式转换:
- 从
&T
到&U
:T: Deref<Target = U>
- 从
&mut T
到&mut U
:T: DerefMut<Target = U>
- 从
&mut T
到&U
:T: Deref<Target = U>
15.3 Running Code on Cleanup with the Drop
trait
在rust中,程序媛可以指定一段特定的代码用于变量out of scope后进行内存释放,rust会自动在需要的时候插入这段代码。而指定这段代码其实就是实现了Drop
trait。drop trait要求实现一个名为drop的方法,并动态指向self。drop释放变量的顺序与创建的顺序相反。
如果想要提前释放某一个变量,可以使用std::mem::drop
fn main() {let c = CustomSmartPointer { data: String::from("some data") };println!("CustomSmartPointer created.");drop(c); //调用std::men::drop,提前释放println!("CustomSmartPointer dropped before the end of main.");
}
15.4 Rc<T>
, the Reference Counted Smart Pointers
有些情况下,有的变量会有多个不同的所有者,比如图论中,多分边指针会指向同一个节点。Rc<T>
就是用来处理这种情况的。RC为reference counting的简写,rc记录着某个值的reference的个数,来决定这个值是否依然被使用着。
enum List {Cons(i32, Rc<List>), //使用rc listNil,
}use crate::List::{Cons, Nil};
use std::rc::Rc; //RC需要显式声明usefn main() {let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));let b = Cons(3, Rc::clone(&a)); //clone一个a的listlet c = Cons(4, Rc::clone(&a));
}
15.5 RefCell<T>
and the Interior Mutability Pattern
Interior Mutablitily允许程序媛修改数据,即便有不可变的指针指向该数据。一般来讲,这个操作是违反borrowing rules的,采用关键字unsafe
可以打破这个规定。
在使用RefCell
时,rust的borrowing rules会在运行时进行检查,而不再是默认的编译时,运行时一些特定的memory-safe scenarios会被允许,也就是说在编译阶段,一些代码上的问题是不能被发现并解决的。
Rc<T>
使得变量得以有多个owner;RefCell<T>
和Box<T>
有单个owner- Box允许在编译时的immutable 或 mutable borrow检查,Rc只允许编译时的immutable检查,RecCell允许运行时的immutable或mutable检查。
- RefCell可以允许程序媛在其内部改动变量,即便RefCell本身是immutable的
Rc<T>
和RefCell<T>
经常组合使用(二者都是作用于单线程的)rc可以允许多owner,但必须都为immutable,refcell允许mutable ref,所以可以用rc<refcell>来实现多owner,同时对变量进行修改
15.6 Reference Cycles can leak Memory
leak memory:有些内存没有被清理释放。Rc和refcell的使用(指针相互指向对方)会导致memory leak。
use std::rc::Rc;
use std::cell::RefCell;
use crate::List::{Cons, Nil};#[derive(Debug)]
enum List {Cons(i32, RefCell<Rc<List>>), //与之前不同,将list的第二项变为可变、多ownerNil,
}impl List {fn tail(&self) -> Option<&RefCell<Rc<List>>> {match self {Cons(_, item) => Some(item), //返回下一个Nil => None,}}
}
当我们使用Refcell包裹Rc时,就会容易出现循环,需要程序媛自己检查代码,rust无法帮我们做到。
另外一种防止出现reference cycle的方法:将Rc<T>转换为Weak<T>
调用Rc::downgrade
,得到一个sp,类型为Weak<T>
,会将weak_count
加一(类似于之前的strong_count)weak_count记录有多少个Weak 指针指向这个变量,区别在于释放这个变量时weak_count不需要变为0。
weak不会带来死循环,代价是rust不能确保weak指向的值依然存在,因此需要程序媛来自行判断。使用upgrade
方法。
初学rust——Iterators、Closures、Crates.io and Pointers相关推荐
- Rust学习—解决crates.io 仓库代码下载慢的问题
今天是学习Rust的第二天,在尝试调用依赖(dependencies)的时候发现非!常!慢!需要采用一些方法来解决. Rust学习-解决crates.io 仓库代码下载慢的问题 今天在下载randra ...
- Rust编译加速crates.io
编译Rust项目时需要访问crates.io, 由于网络环境原因通常比较慢,可以使用国内的crates.io地址: 编辑或新建~/.cargo/config文件,添加以下内容: [source.cra ...
- Rust dependencies依赖管理crates.io原理梳理
1. 背景知识 本文针对的是Cargo 1.37版本. Rust项目的依赖主要在Cargo.toml文件[dependencies]段落中定义,常见的依赖方式有: 基于rust官方仓库crates.i ...
- Rust crates.io换国内镜像源
由于众所周知的原因,crates.io在国内访问十分糟糕,轻则编译缓慢,要很久很久,重则直接超时报错,编译不了. 下面就为rust crates.io换上国内中科大的源 1. 进入当前用户的 .car ...
- rust军用船指令_Rust基础学习笔记(五):Cargo与Crates.io
最近复习鸽了几天,今天继续学,希望在考试之前搞定这个官方文档 もうダメ.... 本章学习Cargo和Crates.io相关,内容有下: 自定义构建 向crates.io提交libraries 利用工作 ...
- Rust学习记录 -> 关于Crates.io的问题
文章目录 前言 问题描述与解析 1.版本更迭带来的依赖包适配问题 2. openssl 总结 前言 最近我在使用rust语言编写一个商场后端demo时,由于需要与mysql进行交互以及序列化等操作,所 ...
- Rust:Cargo check 时 Updating crates.io index 发生错误
Rust:Cargo check 时 Updating crates.io index 发生错误 问题 解决: 问题 Updating crates.io index warning: spuriou ...
- Rust crates.io换源
Rust crates.io换源 问题 Cargo fails with "spurious network error: The operation timed out" on ...
- Rust语言——cargo、crates.io
release profile: 是预定义的 可自定义:可使用不同的配置,对代码编译拥有更多的控制 每个profile的配置都独立于其他的profile cargo主要的两个profile: -dev ...
最新文章
- FTPFileUtil_FTP文件上传 (spring boot)
- 深入了解区块链技术及其常见误区
- LeetCode(合集)删除数组中的元素(26,80,283)
- 关于springboot集成redis及关于redis的Key 乱码问题
- ASP.NET MVC 解析模板生成静态页一(RazorEngine)
- python安装方法3.8.2_Python 3.8.2详细图文安装教程(附安装包)
- 23王道——层次遍历、非递归中序遍历
- 锚具ovm是什么意思_OVM锚具资料
- UOJ #60. 【UR #5】怎样提高智商
- 网络设备:中继器、集线器、网桥、交换机、路由器、网关的超全总结
- Android NDK交叉编译sysstat工具
- 线上问题:java.sql.SQLException: connection holder is null
- win10计算机睡眠 隔几分钟就唤醒,win10系统点击睡眠后又迅速自动唤醒怎么办
- 黑猴子的家:Scala 常用类型
- python打包exe报错编码问题_python打包成exe,但执行exe报错,求解。
- 难溶盐在盐酸中的溶解度分析
- LeetCode 27 合并两个排序的链表
- 故宫珍宝馆完成二期改陈 珍贵红珊瑚盆景揭开面纱
- 基于javaweb+jsp的餐饮店信息管理系统(JavaWeb MySQL JSP Bootstrap Servlet SSM SpringBoot)
- 中软国际的违法罪行,我们用行动来制裁