Rust 复习笔记

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)从当前模块开始,以 selfsuper 或当前模块的标识符开头。

同一文件内,无论私有共有,都可相互调用

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! 可以在最后一个参数指定失败时的输出信息
<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值