Rust
所有权为了管理堆上的内存而生
如果一个类型实现了 Copy 这个 trait, 那么旧的变量在赋值后仍然可用
如果一个类型或这个类型的一部分实现了 Drop trait ,那么 rust 不允许让他再去实现 Copy trait
任何需要分配内存或某种资源的都不是 Copy 的
tuple 是否实现 copy trait 要看它的字段是否都是实现了 copy trait
引用的意思是允许你引用某些值,而不取得其所有权
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BVPoasl9-1640968603233)(C:\Users\Lenovo\AppData\Local\Temp\1640765161745.png)]
把引用作为函数参数的行为叫做借用
在特定作用域内,对某一块数据,只能有一个可变的引用(好处是可以在编译时就能防止数据竞争)
不允许同时拥有一个可变引用和一个不可变引用
切片是不持有所有权的一种数据类型
第一个要记忆的代码
fn main() {
let mut s = String::from("Hello World");
let wordIndex = first_world(&s);
println!("{}", wordIndex)
}
fn first_world(s: &String) -> usize {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}
s.clear() 可把字符串清空
字符串切片就是指向字符串中一部分内容的引用
字符串切片的范围索引必须发生在有效的 utf8 字符边界内
实际上字符串字面值就是切片
一旦 struct 的实例是可变的,那么实例中所有的字段都是可变的(rust 不允许单独声明一个字段可变)
tuple struct 是类似于 tuple 的 struct
- tuple struct 整体有个名,但里面的元素没有名
- 适用:想给整个 tuple 起名,并让它不同于其他 tuple ,而且又不需要给每个元素起名
- struct COlor(i32, i32, i32);
let black = Color(0, 0, 0);
所有的基本数据类型都实现了 std::fmt::Display,因此可以用 {} 来进行输出
要使用 {:?} 或 {:#?} 输出内容时,必须实现 #[derive(Debug)] derive 事实上是派生的意思
枚举允许我们列举所有可能的值来定义一个类型,把枚举可能的值称为枚举的变体
enum IpAddrKind {
V4,
V6,
}
fn main() {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
route(four);
route(six);
route(IpAddrKind::V4);
}
fn route(ipKind: IpAddrKind) {
}
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
fn main() {
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
}
rust 允许将数据附加到欸据的变体中
enum IpAddr {
V4(String),
V6(String),
}
enum _IpAddr_ {
V4(u8, u8, u8, u8),
V6(String),
}
fn main() {
let home = _IpAddr_::V4(127, 0, 0, 1);
let loopback = _IpAddr_::V6(String::from("::1"));
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
}
可用 Impl 块对枚举和结构体定义方法和关联函数
RUST 中有类似于 NULL 概念的枚举 - Option
定义
enum Option<T> {
Some(T),
None,
}
生命变量为 None 时,必须指定 Option 中的 T
let absent_number: Option<i32> = None;
若想使用 Option 中的 T ,必须进行转换(如相加操作时)
if let 和 match 的主要区别是:
if let 只关心一种匹配而忽略其他匹配的情况
fn main() {
let v = Some(0u8);
if let Some(3) = v {
println!("thress")
} else {
println!("others")
}
}
Package -> Crate -> Module
Path 是为 struct、function、module 等项命名的方式
默认情况下,src/main.rs 是一个与包同名的二进制 crate 的 crate 根,src/lib.rs 是与包同名的库 crate 的 crate 根。
crate 是一个模块(module)的树形结构,称为模块树(module tree)。模块可以对 crate 中的代码分组,还可以控制项的私有性,项可以包括任意定义的结构、类型、表达式、变量等,比如结构体、枚举、常量、特性、或者函数。
模块使用 mod
关键字来定义,模块中可以包含其它模块和任意项。即通过使用模块,我们可以将相关的定义分组到一起,并指出他们为什么相关。
路径用于引用模块树中的项
可以通过路径来找到一个模块中包含的项的位置。路径有两种形式:
- 绝对路径(absolute path)从 crate 根开始,以 crate 名或者字面值
crate
开头。 - 相对路径(relative path)从当前模块开始,以
self
、super
或当前模块的标识符开头。
同一文件内,无论私有共有,都可相互调用
use 是用来将路径引入到作用域内(仍遵循私有性原则)
pub mod front {
pub mod hosting {
pub fn add() {
}
}
}
use crate::common::front::hosting;
pub fn eat() {
hosting::add();
}
模块定义时,如果后面是分号的话,rust 会从与模块同名的文件中加载内容,但模块树结构不改变
但是涉及到目录时,则需要创建目录再 mod,这种技术可以让我们把模块内容弄到其他文件中
在作用域中增加 use
和路径类似于在文件系统中创建软连接(符号连接,symbolic link)。
为了在 rust 模块中找到某个条目,需要使用路径
路径的两种形式
- 绝对路径:从 crate root 开始,使用 crate 名或 字面值 crate
- 相对路径:从当前模块开始,使用 self、super 或当前模块的标识符
如果想把一些东西设置为私有的话,可以将其放到某个模块中
默认情况下,rust 中的所有条目都是私有的
可以使用 use 关键字将路径导入到作用域内
as 关键字可以为引入的路径指定本地的别名
使用 pub use 重新导出名称
使用 + 进行连接字符串时,实际上是类似这个签名的方法
fn add(self, s: &str) -> String {}
let s1 = "hello ".to_string();
let s2 = "world".to_string();
let s3 = s1 + &s2; // 使用了解引用强制转换这项技术,使 s2 的引用变成字符串切片
推荐使用 format!() 因为不会取得所有权
rust 字符串不支持索引语法进行访问
事实上 String 是 Vec 的封装
use std::collections::HashMap;
fn main() {
let terms = vec!["blue".to_string(), "yellow".to_string()];
let initial_scores = vec![10,50];
let scores: HashMap<_, _> = terms.iter().zip(initial_scores.iter()).collect();
println!("{:#?}", scores);
}
安全插入数据
scores.entry(String::from("blue")).or_insert(50);
Box<dyn Error> 可简单理解为:任何可能的错误对象
fn largest<T: PartialOrd + Clone>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list.iter() {
if item > &largest {
largest = item
}
}
largest
}
fn main() {
let str_list = vec![String::from("hello"), String::from("world")];
println!("{}", largest(&str_list))
}
rust 的生命周期声明取生命周期最短的那一个的
指定生命周期参数的方式依赖于函数所做的事情
结构体生命周期生命中,字段的生命周期必须比结构体的长
struct Import<'a> {
part: &'a str,
}
impl<'a> Import<'a> {
}
在 struct 上使用生命周期实现方法,语法和泛型参数的语法一致
rust 每个引用都有自己的生命周期
生命周期:使引用保持有效的作用域
大多数情况下,生命周期是隐式可推断的
当引用的生命周期可能以不同的方式互相关联时,需要手动标注生命周期
生命周期存在的一个重要目标是避免垂悬引用
借用检查器:比较作用域从而判断所有的借用是否合法
本章精髓内容
生命周期省略的三个规则:
- 规则一:每个引用类型的参数都有自己的生命周期
- 规则二:如果只有 1 个输入生命周期参数,那么该生命周期被赋给所有的输出生命周期参数
- 规则三:如果有多个输入生命周期参数,但其中一个是 &self 或 &mut self (是方法),那么 self 的生命周期会被赋给所有的输出生命周期参数
测试实际上就是一个函数,测试非测试代码的功能是否和预期的一致
测试函数体(通常)执行的三个操作:
准备数据/状态
运行被测试的代码
断言(Assert)结果
在函数上面加上 #[test] 标注,即可把函数变为测试函数
使 mod 变成测试模块,#[cfg(test)]
必须知道的知识
测试函数 panic 就表示失败
每个测试运行在一个新线程
当主线程看到某个测试线程挂掉了,那个测试就标记为失败了
断言
assert!(false); // 断言是否成功
assert_eq! 和 assert_ne! 用来测试相等性
assert!、assert_eq!、assert_ne! 可以在最后一个参数指定失败时的输出信息
<