-
Notifications
You must be signed in to change notification settings - Fork 0
Rust一览
Zheng Ping edited this page Mar 22, 2019
·
33 revisions
- rust是一个基于表达式对语言
- 字面常量,整数
1
,浮点数1.2
,字符'a'
,字符串"abc"
,布尔true
和单元类型()
- 内置类型,整数
u8, i8, u16, i16, u32, i32, u64, i64, is (isize), us (usize)
,浮点数f32, f64
,布尔bool
,字符类型char
是一个Unicode标量值,字符类型不同于字符串; rust中的数据类型分为标量(scalar)数据和复合(compound)数据,标量(scalar)类型代表一个单独的值,复合(compound)类型可以将多个值组合成一个类型,rust提供两个原生的复合类型: 元组(tuple)和数组(array); - rust中基础类型间不能隐式转换,类型转换需要用关键字
as
- rust编译器默认开启属性
#[warn(unused_variable)]
,会对没有使用的变量发出警告。rust允许先声明变量然后再初始化,但是使用未被初始化的变量会产生一个编译时错误 - rust禁止使用未初始化的变量, 变量与常量是不同的概念, 常量用
const
声明, 声明常量必须注明值类型, 常量只能被设置为常量表达式而不能是其它函数调用的结果; - rust中
if
是一个表达式,它会返回一个值,并且if-else
语句的每个分支必须返回相同的类型,以保证类型安全, 因为if
语句是表达式,所以let
语句的右侧可以跟if
- 语句是代码块(block)的组成部分, rust中只有两种语句(声明语句和表达式语句),声明语句分为声明某个item(比如函数)和
let
声明两种, 其它的都是表达式(表达式结尾没有分号, 创建新作用域的大括号{}
也是表达式). 表达式语句以分号结尾时要特别注意,它的作用就是将表达式变成语句,分号有一层含义就是忽略该表达式的值,返回元类型()
- 绑定是rust中的一种语句,也就是声明语句,rust用
let
来引入一个绑定而不是表达式,当通过let
绑定进行模式匹配或解构时,ref
关键字可以用来表示对结构或元组中某个字段的引用 - rust中一个赋值分配的返回结果是元
()
类型 - 函数参数有两个术语: 形参和实参. 函数形参必须标识类型,函数只能返回一个值; 使用
return
关键字和指定值,可从函数中提前返回;但大部分函数隐式返回最后的表达式. 函数中返回变量(非变量的引用), 变量的所有权会被移动出去, 它的值也就不会随着函数作用域的结束而被释放 - rust中的循环有
loop
,while
和for
,loop
本身也是一个表达式, 它的一个用例就是重试可能会失败的操作,break
语句除了中断循环外还有类似return
的返回值的功能(用break
返回值是一个语句, 所以语句结尾需要跟分号); rust中两种主要的循环结构:for
和while
,无限循环用loop
,无限循环可以被给予一个标签名,比如'outer: loop {
,标签名可用于跳出或继续无限循环 -
..
是rust中的range
操作符, 注意它的左右边界的定界规则以及它在特殊情况下的写法; 注意字符串slice``range
的索引必须位于有效的UTF-8字符边界内,如果尝试从一个多字节字符的中间位置创建字符串slice
,则程序将会因错误而退出; 字符串slice
的类型声明写作&str
; 字符串字面量就是&str
类型;slice
的类型声明通常表示成&[type]
(注意引用符号), 比如&[u8]
,slice
不同于数组, 数组存储在连续的内存区中, 它的大小在编译期间就已确定, 数组标记为[T; size]
- 与C/C++不同,rust中没有规定函数的定义顺序,无返回值的函数可以把返回值声明为元类型
()
,函数名也可以作为一个变量使用,它实质上是一个函数指针,比如let bar: fn(i32) -> i32 = foo;
,这句话中,foo
是函数名,bar
和foo
都可视为函数指针 - 发散函数(diverging function,返回值是
!
)不会返回,可用于任何类型(即发散函数可以绑定到任何值) - rust中的注释分文档注释和行注释两种
- 元組是大小固定的有序列表,元组中的元素可以是不同的类型,函数返回元組是一个夹带多返回值的好方法,元组的值可以通过
valN
字段访问,如果一个元组只有一个元素,那么该元素后面必须
带上一个逗号,比如(5u,)
,因为(5u)
会被当作整数对待 - 要让结构的实例是可变的,则只能把这个实例声明为
mut
,结构定义中的某个字段不能被声明为mut
; 为结构体定义方法用impl {}
语句块(可以用多个impl {}
来定义一个结构); 构建结构体实例时用字段初始化简写语法(field init shorthand)和结构更新语法(Struct Update Syntax)更简洁; 元组结构(tuple struct
)没有字段名, 只有字段类型(比如struct WriteMessage(String);
就是一个元组结构), 元组结构体可用于区分不同的元组类型; 没有任何字段的结构是类单元结构(Unit-Like Struct
); 结构体的属性也有公有、私有的限制,模块定义时要明确结构体属性的可访问性; 编程时要特别注意结构体实例的所有权,要确保结构体中字段所持有的数据的有效性跟结构体实例本身保持一致; - 枚举类型表示“从成员类型中选一种”(
one of the types
),枚举成员可以关联数据(枚举的每个成员可以关联不同类型和数量的数据), 枚举没有默认的访问操作符,定义一个枚举就是定义了一个新类型,枚举在范型跨类型的时候很有用(rust中的枚举更像函数式编程语言中的代数数据类型(algebraic data types)); 枚举也可以像结构那样定义方法; rust调用方法时,提供了一个自动引用和解引用(automatic referencing and dereferencing
)的功能,比如当rust中使用obj.fn()
时,rust会自动为obj
添加&
,&mut
或*
以使obj
与方法名匹配,这是因为每个方法都有明确的接收者的所有权约束(也就是约束self
的所有权); 在impl
块中不以self
作为第一个参数的函数是关联函数(associated functions),它不作用于一个对象实例,因而它不是方法(方法为对象提供行为),调用关联函数时用结构名和::
符号来调用(比如String::from()
); - 枚举
Option
有两个变体:1.None
表明失败或者没有值;2.Some(value)
是一个T
类型的value
的元组结构的封装;rust在语言层面不提供空值(null
)功能,Option
表示要么有值要么没值,Option
回避了null
可能会引发的问题(限制空值泛滥而增加安全性), 它让我们在使用前先要判断是否有无值; 使用Option
编程时可以直接使用Some
或None
,Option::
前缀可以直接省略;Option
在做指针非空性检查时很有用; 与Option
类似的枚举是Result
,Result
用于错误处理; -
match
是一个表达式,它通过匹配模式来执行代码,强调穷尽(exhaustive)检查,注意对_
的匹配.match
模式匹配非常强大,它允许我们得到来自枚举中的值,既可以多值匹配,又可以范围匹配(比如n @ 13 ... 19 =>
),甚至还可以通过if let
和while let
来绑定进行条件匹配,当用匹配来解构结构体时,如果只想匹配结构体中的某个属性,需要用..
符号来忽略掉其它的属性,如果不使用..
符号,可能会导致匹配错误;解构时可以通过&
、ref
或ref mut
得到解构值的引用; 可以认为if let
是match
的一个语法糖, 如果只想匹配单个情况,if let
比match
更合适;match
表达式中用return ...
表示返回函数的值, 而某个分支的最后一个表达式则表示是match
的值; -
const
通常表示一个值,static
通常表示内存地址,static很少用到,它通常用于全局锁,全局原子计数以及和C库的衔接。"string"
文法有点特殊,它可以直接被赋给static
变量,因为字符串的类型签名(type signature)本来就是&'static str
- rust的两个主要字符串类型:
String
和&str
(一个在栈中, 一个在堆中),所有的字符串可以被有效的编码为UTF-8序列,与C不同,字符串不是以null结尾的,可以包含null字节。&str
被叫做字符串切片,是一个指向静态分配字符串的引用(&),它有固定大小,不可变,String是堆分配字符串,它是可变的,实际上是存储字节的向量,它分配内存控制它的数据,并且保证是UTF-8编码,如果你需要非UTF-8
字符串,可以使用OsString
,因为rust中字符串存在三种视角: 字节(byte
)、标量值(scalar value
可以调用chars()
)和字形簇(grapheme cluster
),所以String
不能进行索引操作,注意字符串slice``range
的索引必须位于有效的UTF-8字符边界内,否则会panic
;遍历字符串可以通过字符串的字节或标量值来遍历,字形簇比较复杂,标准库不提供对字形簇的操作;String
转化成&str
很廉价,但是反向转化有点昂贵,&'static str
表示一个只读内存区字符串的引用;字符串比较复杂,rust程序员必须思考如何预先处理UTF-8数据; - 数组是固定大小的同类型元素的列表,可变的数组需要用关键字
mut
修饰,有一种简单的初始化数组的写法[T;N]
,向量总是在堆上分配数据,它的大小是可变的,可以使用宏vec!
创建向量。切片就是引用一个序列中的部分连续数据,它无需拷贝,切片是&[T]
类型 -
std::collections
的数据都存储在堆上, 使用std::collections
之前要考虑的第一个问题是容量(capacity
), 比如with_capacity()
方法可以创建具有指定容量的集合; 遍历集合时主要使用三种迭代器iter
、iter_mut
和into_iter
; 迭代器也可以在部分使用后丢弃(break
), 这样能防止计算未使用的项目;entry
方法在无值插入填充时非常方便; 向量只能存储相同类型的值; 向量是可调整大小的数组,它的大小在编译时是未知的,一个向量可以用三个单词描述:一个指向数据的指针,长度和它的容纳能力; 使用向量时注意Vec
和vec!
的使用, 引用向量中的元素可以使用[]
操作符和get()
方法, 向量可以无需iter()
被直接遍历(比如for i in &mut vec { ... }
);HashMap
可以把任何类型的数据作为键值, 类似于向量,HashMap
的键的类型必须一致, 值的类型也必须相同, 通过向量来构建HashMap
可以用到.iter().zip().collect()
, 往HashMap
中添加键值对时要注意,如果不存在深拷贝(deep copy
),HashMap
可能会接管数据的所有权,遍历HashMap
用for (k, v) in &map { ... }
,获取值用get()
或entry()
方法, 注意entry()
通常用于无值时填充初始值,HashMap
可以定制哈希函数(hasher
); -
panic!
是一个宏, 它表示不可恢复的错误, 在程序中触发不可恢复的错误可以调用panic!
, 它会展开(unwinding)堆栈退出或直接终止(abort),panic!
具有阻止编译器检查返回值类型的作用, 在debug
模式下用panic!
来调试堆栈(需要设置RUST_BACKTRACE
环境变量)是很方便的; 处理可恢复的错误用枚举Result
,Result
附带的方法map_err
和unwrap_or_else
可以让错误处理的编写更加平滑,unwrap
和expect
方法可以不写match
就返回Result
中的正确结果并在错误时panic!
,?
操作符非常适用于需要错误传播的场景, 它在正确时返回正确值作为表达式的值, 错误时直接return Err
(或在无值时return None
)作为整个函数的返回值,?
操作符非常适用于书写链式调用, 另外有一个限制,?
符号只能用在返回值类型为Result
或Option
的函数中; 代码原型和测试非常适合panic!
(此时可以多使用unwrap
和expect
),panic!
非常适合检查不能被控制的外部参数; -
范型(
Generic Data Types
)是类型参数化, 范型的类型参数声明写在尖括号中, 引入范型是为了减少重复代码; 范型结构的方法签名必须带上类型参数, 如果没有类型参数而传入具体的类型参数就只能表示某个确切的类型, 比如为impl<T> Point<T> {...}
和impl Point<f32> {...}
定义的方法表示两种不同的定义; 结构体定义中的泛型类型参数并不总是与结构体方法签名中使用的泛型是同一类型(例如impl<T, U> Point<T, U> { fn mixup<V, W>(&self, ... }
); rust在编译范型代码时使用单态化技术来保证目标代码的执行效率; -
trait
以一种抽象的方式为多个类型定义共享行为,trait bounds
可以通过绑定trait来约束范型参数需要遵循特定的行为,trait
在某些层面类似于接口; 声明trait
的语法是trait xxx { ... }
,trait
中可以有默认的方法实现也可以只声明方法签名,trait
中的默认方法的代码可以调用方法签名, 但是无法从同名方法的重载实现中调用trait
中的默认方法; 为类型实现trait
的语法是impl Trait for ts {...}
, 实现trait
时要注意只有当trait
或者要实现trait
的类型位于crate
的本地作用域时, 才能为该类型实现trait
, 但是不能为外部类型(即外部包定义的类型)实现外部trait
(为了保证代码安全性); 可以通过impl Trait
或trait绑定(也就是范型尖括号语法)来约束函数的参数是实现了某些trait
的类型,trait
绑定可以约束两个实现了同trait
类型的参数是否是同一类型, 如果参数同时需要实现多种trait
可以通过加号(+
)连接, 为了优雅的代码可以用where
语法来对trait绑定进行类型说明; trait绑定还可以通过对范型参数的trait
约束有条件地实现trait
中的方法, 也可以对任何实现了特定trait
的类型有条件地实现trait; 函数的返回值也可以用impl Trait
加以限定(这只适用于返回单一类型的情况);trait
是针对未知类型Self
声明/定义的方法集, 任意的数据类型都可以实现trait
,Self
表示实现了当前trait
的数据类型(self
是类型对象);编译器已经通过#[derive]
属性提供了一些简单的trait
实现,比如#[derive(Debug)]
;通过专门的语法糖可以实现操作符重载,比如add方法就是Add
trait的一部分;当对泛型使用trait的时候,比如Ty: Tr
(读作Ty必须实现Tr特性(trait)),Tr实际上是到Ty的一种绑定,这种绑定有两种方式:1. 在类型的第一个实例处应用绑定;2. 直接在impl
的{
之前的where
子句中使用;Drop
这个trait类似于析构函数,它会在对象越过作用域时调用,Drop
这个trait可以被任何自定义数据类型实现,也可以通过调用drop
函数来手工释放实现了Drop
这个trait的对象的资源(这样可以在作用域还没结束前就释放资源);Iterator
这个trait用于在集合和惰性值生成器上面实现迭代器;Iterator trait只要求定义next
方法,next方法总是返回Some
,并且它的返回类型是Option
;Clone
这个trait是资源转移的反面特性,它是对资源数据进行一份复制(deep copy) - rust中每个引用都有其生命周期(
lifetime
), 生命周期就是引用的有效作用域, 大部分时候生命周期是隐含可推断的, 这时就不需要注明; 生命周期避免了悬挂引用; rust编译器会通过检查代码块的作用域(由借用检查器(borrow checker
)来完成),追踪被引用对象的生命期来确保租借(borrow
)的安全性,rust中生命期的写法'burrito
,它读作生命期burrito
,所有的引用都有一个&'a T
形式的类型签名(type signature),'a
就是被引用对象的生命期,编译器会看管好插入的生命期部分'a
,所以我们只需要简单地把引用写成&T
,类型签名&'a T
读作引用一个有生命期a的T类型,'static
生命期会持续到程序的结束,有两种情况可以产生'static
生命期的变量:1. 通过"string"
文法来进行绑定,因为"string"
的类型签名是&'static str
;2. 直接用static
关键字来产生变量; -
cargo
是rust中的中方便解决依赖的程序建立工具,有时它可以被认为是一个rust风格的包管理/建立工具 -
use
关键字往作用域内导入名字(可以想象成import namespace
, 类似于文件系统中的软连接),在包(cargo
)内部导入名字在写法上也分绝对路径和相对路径, 注意相对路径写法要跟上self
或super
关键字(导入外部包的模块无需self
和super
关键字). 推荐只导入模块而不要直接导入函数(通过模块名来调用函数清晰地表明该函数不是本地定义的),这样做能更好的避免命名冲突(也可以通过as
关键字来避免命名冲突);导入来自同一模块的多个名字用大括号更简单,比如use phrases::english::{greetings, farewells};
和的use std::io::{self, Write};
(其中self
指代std::io
);pub use
可以把模块内的公开内容导入进另一个模块,这样能把模块的内容做层级调整,比如真实的模块结构A::B::C
我们可以把它公开成A::C
,但实际上两者是同一个东西(这就是所谓的重导出(re-exporting
)); 注意use self::
表示当前模块,use super::
表示当前模块的父模块,另外foo::bar()
和::foo::bar()
也是不同的; glob运算符*
可以把某个路径下的所有公用项引入作用域, glob运算符经常用于测试模块tests
中; 不同层级的模块同样可以编码在按层级组织的文件中; 导入外部包需要先在Cargo.toml
中声明,然后再use
使用,std
随rust语言分发,无需在Cargo.toml
中声明可以直接使用 - 模块是一系列的函数、结构、trait、impl或者其它模块的集合。定义模块使用
mod
关键字,模块是rust中私有性的边界,要注意模块里的条目的公私有问题,默认是私有的(在默认的非pub
限定下, 从当前模块向**"下"看时是私有的,不过当你向"上"看时是公有的, 即不允许使用定义于当前模块的子模块中的私有代码, 允许使用任何定义于父模块或当前模块中的代码),模块通过路径(分绝对路径和相对路径**, 模块的绝对路径以crate
开头)来组织模块结构, 使用模块时要注意路径的取向问题,比如默认的根crate路径,当前模块的位置路径self
,父模块的位置路径是super
,使用相对路径意味着模块的结构调整时变动的代码更少(比如使用super
); 结构的成员可以用pub
进行细分约束, 但是枚举(enum
)的公私有属性只取决于枚举自身 - 一个
crate
可以被编译成二进制文件或库文件,默认会编译成二进制文件; 每个crate
都有一个隐藏的crate
根(crate root
描述如何构建crate
的文件),在根模块下可以定义一个子模块树,crate
中的条目(item)通过模块组织在一起; 一个包(cargo
)中可以有零个或一个库crate
和多个二进制crate
, 一个包中必须至少有一个(库或者二进制)crate
;extern crate ...
表明我们要编译链接外部的crate
; 一个crate
就是rust中的一个编译单元,当你用rustc
编译单个文件时,这个文件也会被当作一个crate
对待,如果一个源文件中定义了模块,在编译前模块的内容会先被合并到crate
,也就是说模块不会被单独编译 - rust对测试的支持比较友好,这个是保证程序质量的重要手段
- 指针的value是内存中某个的地址值,在打印宏中(println!)通过
{:p}
可以输出它的地址,当指针在指向不在栈内存中的地址时十分有用;:?
符号告诉println!
使用Debug
输出格式; -
#cfg
类似于C中的ifdef
宏,它会根据传入的编辑器标志进行条件编译 - 迭代器中的引用
let nums = vec![1, 2, 3];
for num in &nums {
println!("{}", num)
}
-
::<>
符号会给出类型暗示,比如let one_to_one_hundred = (1..101).collect::<Vec<i32>>();
- 符号
_
有时被称为类型占位符(type placeholder)
,它通常表示推导类型
的意思,_
不能被用在项目签名(item signature)里,因为不允许全局引用,像Vec<_>
被称作局部类型暗示,它在组合数据时比较有用,比如像迭代器上面的collect()
- rust的类型推导(type inference)系统不仅能根据初始化语句里的右值推导类型,还能根据后续的使用推导类型
- 可以用
type
关键字定义某个类型的别名,它类似于C语言中的typedef
,要注意类型名称必须用骆驼命名法(即所有单词首字母大写),只有基础类型除外,类型别名的一个主要用途是得到更简短的名称 - rust中属性是应用于(可理解为修饰)
crate
、模块(mod)或条目(item)的元数据,主要用于:实现条件编译,设置crate名字、版本及类型,取消可疑代码的警告,设置编译器选项,链接外部库,标记单元测试函数,把函数标记基准测试的一部分。属性有两种语法,#![crate_attribute]
应用于整个crate
,而#[crate_attribute]
应用于近邻的一个模块或条目。属性的参数有三种形式:#[attribute = "value"]
,#[attribute(key = "value")]
和#[attribute(value)]
。属性类似于C中的宏,比如#![crate_type = "lib"]
定义crate
的编译类型是库,而cfg
属性可用来进行条件编译,比如#[cfg(target_os = "linux")]
指明某个项目只在linux平台上编译 - 闭包的参数放在两个管道操作符
|
之间,闭包代码块中的最后一个表达式的值就是闭包的返回值 -
iterator给你一系列连续的值,它是一个惰性的值生成器(lazy value generator),iterator adapters会对一个迭代器(iterator)进行操作,并产生一个新的迭代器,consumer会操作一个迭代器,产生一个最终的结果集(有点像生成器),
collect
,find
和fold
是常见的consumer - rust中的结构和函数都支持泛型,使用泛型时把类型参数放在尖括号对中即可,比如
struct Pair<T> {
,当调用泛型函数时,需要用::
符号,比如swap::<char>(param)
- rust中的所有值默认在栈中分配,
box
可以用来在堆中分配内存,box类型的数据是一个指向堆中内存区的职能指针,比如Box<T>
表示指向堆内存区中一个T类型数据的指针,当box的生命期结束后,内部对象会被销毁,它指向的堆中的内存会被释放,取得box指向的值有两种方法:1.用操作符*
(解引用操作符);2.用let box y = x
这种匹配模式来解构 - rust遵循RAII(资源获取就是初始化)规则,一旦对象的作用域结束,它的析构器就会被调用(调用
drop
函数, 类似于C++的destroy
),变量所拥有资源也会被释放。 - 资源只能有一个属主(所有权是rust的核心),因为如果一个资源有多个属主,那么一个资源就可能会被释放多次。在rust中,当对
Box<T>
使用let y = x
或把y
值作为参数传递给函数时,资源的所有权将会转移。当资源发生转移后,之前的资源属主将不能被使用(不再拥有堆中的那块内存区)。但是当资源所有权发生转移时,资源数据的可变性(mutability)却可以改变 - 不想通过属主访问某个内存资源,这正是rust的借用(
borrow
)机制的用武之地。借用就是C中的引用&
,借用资源越过作用域后,资源不会被销毁,被借用过的资源还可以在外部继续使用,被借用的资源也存在可变性(mutability)的约束,可变的借用&mut T
,不可变的借用不能传给可变的借用,但是可变的借用可以传给不可变的借用。被引用的资源在引用变量未消亡前会被冻结,当资源被冻结时,通过资源属主来修改数据会出错。资源可以被多次只读借用,但是当资源被只读借用后,它就不能再被可变借用;与之相反,在一个作用域里,可变借用只能发生一次,当可变借用消亡后,原始数据资源才能被再次借用 - rust中所有权的存在就是为了管理堆(
heap
)数据(比如回避二次释放带来的危害). rust中每一个值都有一个被称为其所有者(owner)的变量; 一个值有且只有一个所有者; 当所有者(变量)离开作用域,这个值将被丢弃(通过调用drop
函数被释放, 类似于C++中的destroy
); 由于所有权的存在所以要考虑变量的传递是否触发了浅拷贝(shallow copy
, 进行变量传递赋值时, 如果没有发生深拷贝(deep copy
), 其值就会发生移动(就会出现变量失效的现象, 标量类型变量的传值就不是浅拷贝), 此时要考虑是否会转移值或是否需要克隆值; 引用正是为了解决变量传递时因值转移而导致失效的问题, 引用不会获得值的所有权, 当引用离开作用域时其指向的值不会被丢弃; 用引用来作为函数参数称为借用(borrow); 同一作用域里对一个变量只能
有一个可变引用, 同一个变量可以有多个不可变引用, 同一个变量的不可变引用和可变引用不能混用(比如传参时, 当拥有某值的不可变引用时, 就不能再获取一个可变引用), 这些限制都是为了避免数据竞争(data race
);slice
是引用集合中一段连续的元素序列, rust提供引用
和slice
的目的就是为了不拥有数据的所有权; - 所有权、转移、借出和生命期:1.默认,Rust在栈上分配内存(包括结构体),要在堆中分配,必须使用box修饰符;2.默认,Rust对栈上空间变量的再赋值,都是copy的(包括结构体);3.对堆上空间,使用 let x = y 的形式,资源的ownership会从y转移到x去。转移后,之前的拥有者就不能再使用(读写都不允许);4.当ownership转移的时候,mutability是可以从imutable变成mutable的;5.租借就是在不改变资源的属主的情况下,使用资源的一种方式;6.一个资源只能有一个属主,可以有一个或多个租借者;7. Rust中只要一看到&号,就肯定是租借(与c交互的unsafe代码除外);8. 如果&号出现在结构体(域),函数(参数,返回值),闭包(引用捕获)中,那这些结构体,函数,闭包本身就是一个租借者;9. 资源的属主要负责资源的销毁;10. 资源的属主可以出租资源(以可变或不可变的形式);11. 资源属主可以转移拥有关系;12. 资源一旦以不可变出租,owner就不能再改变资源的内容(owner还是能读),也不能再以可变的形式出租,但可以以不可变的形式继续出租;13. 资源一旦以可变出租,属主就不能再访问资源内容(读写都不可以),也不能再出租给其它绑定(这种出租具有排它性);14. 不可变租赁者可以将对资源的租赁分享(复制)给另一个不可变租赁者;15. 可变租赁者可以将对资源的租赁转移(move)给另一个可变租赁者;16. 租赁者自己的生命期一旦结束(在不使用租借链传递scope的情况下),就自动将资源的使用归还给属主。属主自动恢复之前的权力;17. 变量不能绑定到比自己生存期小的资源上面;18. 四种对象可以称为租借者:变量、函数、结构体和闭包;
- 租借期:1. 租借期指的是租借生效的这个scope;2. 租借期是可以通过分享、转移(move)来扩展的,它本身并不是lifetime;3. 公式(牢记):resource scope >= borrow scope = union of all borrowers’ scopes | 资源生存期(资源拥有者生存期) >= 租借期 = 各种租借者生存期的并集;4. 租借期不是资源的属性,也不是租借者对象的属性,是一种独立存在的过程属性。但它可以依附在某一个租借者对象上面来做检查;5. 租借期这个属性只能增长,不能缩小(如果一个租借者身上附着了一个长的租借期,再给它绑定一个新的较短生存期的资源,编译器会报错)6. 资源有自身的生命期(lifetime),租借者也有自身的生命期,租借期跟这两种生存期意义都不一样,要注意区分。
- 并发并非rust语言的特性,它依赖于标准库,如果你不喜欢rust的并发,你也可以实现你自己的并发方式