学习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:

  1. FnOnce 从其scope中获取变量,并得到变量的ownership,将其move到closure中。Once表示闭包无法超过一次获得同个变量的ownership
  2. FnMutmutably borrow,从而可以改变环境
  3. Fnimmutably 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实现DerefDroptraits。其中Deref允许一个sp的实例拥有reference的功能,Drop能够帮助程序媛在sp超出scope后去customize代码。

本章会涉及rust中最常见的标准库中的sp:

  • Box<T> 将数据分配到堆上
  • Rc<T> reference counting type,enable多个ownership
  • Ref<T>RefMut<T>accessed through RefCall<T>,将borrowing规则施加在运行时而不是编译时。

除此之外还会涉及interior mutabilityreference 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&UT: Deref<Target = U>
  • &mut T&mut UT: DerefMut<Target = U>
  • &mut T&UT: 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相关推荐

  1. Rust学习—解决crates.io 仓库代码下载慢的问题

    今天是学习Rust的第二天,在尝试调用依赖(dependencies)的时候发现非!常!慢!需要采用一些方法来解决. Rust学习-解决crates.io 仓库代码下载慢的问题 今天在下载randra ...

  2. Rust编译加速crates.io

    编译Rust项目时需要访问crates.io, 由于网络环境原因通常比较慢,可以使用国内的crates.io地址: 编辑或新建~/.cargo/config文件,添加以下内容: [source.cra ...

  3. Rust dependencies依赖管理crates.io原理梳理

    1. 背景知识 本文针对的是Cargo 1.37版本. Rust项目的依赖主要在Cargo.toml文件[dependencies]段落中定义,常见的依赖方式有: 基于rust官方仓库crates.i ...

  4. Rust crates.io换国内镜像源

    由于众所周知的原因,crates.io在国内访问十分糟糕,轻则编译缓慢,要很久很久,重则直接超时报错,编译不了. 下面就为rust crates.io换上国内中科大的源 1. 进入当前用户的 .car ...

  5. rust军用船指令_Rust基础学习笔记(五):Cargo与Crates.io

    最近复习鸽了几天,今天继续学,希望在考试之前搞定这个官方文档 もうダメ.... 本章学习Cargo和Crates.io相关,内容有下: 自定义构建 向crates.io提交libraries 利用工作 ...

  6. Rust学习记录 -> 关于Crates.io的问题

    文章目录 前言 问题描述与解析 1.版本更迭带来的依赖包适配问题 2. openssl 总结 前言 最近我在使用rust语言编写一个商场后端demo时,由于需要与mysql进行交互以及序列化等操作,所 ...

  7. Rust:Cargo check 时 Updating crates.io index 发生错误

    Rust:Cargo check 时 Updating crates.io index 发生错误 问题 解决: 问题 Updating crates.io index warning: spuriou ...

  8. Rust crates.io换源

    Rust crates.io换源 问题 Cargo fails with "spurious network error: The operation timed out" on ...

  9. Rust语言——cargo、crates.io

    release profile: 是预定义的 可自定义:可使用不同的配置,对代码编译拥有更多的控制 每个profile的配置都独立于其他的profile cargo主要的两个profile: -dev ...

最新文章

  1. FTPFileUtil_FTP文件上传 (spring boot)
  2. 深入了解区块链技术及其常见误区
  3. LeetCode(合集)删除数组中的元素(26,80,283)
  4. 关于springboot集成redis及关于redis的Key 乱码问题
  5. ASP.NET MVC 解析模板生成静态页一(RazorEngine)
  6. python安装方法3.8.2_Python 3.8.2详细图文安装教程(附安装包)
  7. 23王道——层次遍历、非递归中序遍历
  8. 锚具ovm是什么意思_OVM锚具资料
  9. UOJ #60. 【UR #5】怎样提高智商
  10. 网络设备:中继器、集线器、网桥、交换机、路由器、网关的超全总结
  11. Android NDK交叉编译sysstat工具
  12. 线上问题:java.sql.SQLException: connection holder is null
  13. win10计算机睡眠 隔几分钟就唤醒,win10系统点击睡眠后又迅速自动唤醒怎么办
  14. 黑猴子的家:Scala 常用类型
  15. python打包exe报错编码问题_python打包成exe,但执行exe报错,求解。
  16. 难溶盐在盐酸中的溶解度分析
  17. LeetCode 27 合并两个排序的链表
  18. 故宫珍宝馆完成二期改陈 珍贵红珊瑚盆景揭开面纱
  19. 基于javaweb+jsp的餐饮店信息管理系统(JavaWeb MySQL JSP Bootstrap Servlet SSM SpringBoot)
  20. 中软国际的违法罪行,我们用行动来制裁

热门文章

  1. 递归整理及几个经典题目
  2. Ubuntu系统如何搭建可视化界面
  3. 6、分析Linux内核创建一个新进程的过程
  4. 如火如荼的钉钉短视频选用了趣拍SDK
  5. Django--在线相册管理系统(2)
  6. 【原创】基于SSM框架的电子相册管理系统网站设计与实现
  7. IDEA--IDEA debug断点调试技巧
  8. siki学院_Unity初级案例_愤怒的小鸟_学习笔记2/3
  9. 关于视频压片的一些解释
  10. 窄带物联网,开启万物互联新篇章