估计也只有我能看懂了
参考: Rust 程序设计语言
https://kaisery.github.io/trpl-zh-cn/foreword.html
参考: Rust 编程之道
https://weread.qq.com/web/reader/0303203071848774030b9d6
Rust 死灵书
https://www.bookstack.cn/books/rustonomicon_zh-CN
BiliBili令狐一冲
https://space.bilibili.com/485433391
练习Demo
use std::collections::HashMap;
use mylib::utils::students::Student;
use crypto::digest::Digest;
use std::fs::File;
use std::io;
use std::io::{Read, Write, BufRead};
use std::fmt::{Display, Debug};
use std::rc::{Rc, Weak};
use std::cell::RefCell;
fn print<T>(args:T)
where T: Any + Debug
{
println!("{:?}",args)
}
mod handle1 {
pub mod people {
pub fn say(){
println!("人 打招呼")
}
}
pub mod dog {
pub fn eat() {
println!("狗 吃骨头")
}
}
}
fn stu1() {
println!("stu1: 变量");
// 1. 变量的声明
let a: i32 = 123456789;
// 2. 变量 (变量默认不可以改变)
// a = 2233 // error: cannot assign twice to immutable variable
// 3. 变量遮蔽 (变量的重新声明赋值,再次使用 let 时, 实际上创建了一块新内存,并绑定变量)
let a: i32 = 2233;
// 注意变量这比并不会导致原本变量的生命周期消失, 直到当前作用域结束, 自动释放, 这在后面可以知道
// 4. 可变变量 (添加 mut 关键字, 使变量可变ps:修改原有内存上的值?)
let mut a: i32 = 3344;
a = 4433;
// a = '1' // 注意 mut 不可以修改类型
// 5. 常量 (一般以大写声明, 类似于不可变变量, 但是常量不可以使用 mut, 声明常量使用 const, 且 必须注明值得类型, 常量可以在任何作用域声明包括全局作用域, 常量不能作为函数的返回)
const MAX_NUM: u32 = 2233;
}
fn stu2() {
println!("stu2: 数据类型");
// 1. bool型
let is_true: bool = true;
let is_false: bool = false;
// 2. char (单引号, 在 rust 里面, char 占 4 个字节, 是 32 位的)
let a: char = '你';
let a: char = 'h';
let a: char = 'ß';
// 3.1 数字类型 (允许使用 _ 做为分隔符以方便读数, i8 i16 i64 u8 u16 u32 u64 f32 f64...)
let a: i128 = -22_223; // 有符号 32 位
let a: u32 = 22_333; // 无符号 32 位
let a: f32 = 0.618; // 浮点型 (rust 中浮点只有 f32 和 f64)
// 3.2 数字类型扩展: 运算符
let a: u32 = 10 + 2;
let a: u32 = 3 - 1;
let a: u32 = 2 * 4;
let a: f32 = 56.7 / 32.2; // 注意! 整形无法相除得到浮点型
let a: i32 = 56 / 32; // 整形相除只能得到整形
// 4. 自适应类型 (usize isize, 根据操作系统的不同有不同的值)
println!("usize max: {}", usize::max_value());
println!("isize max: {}", isize::max_value());
// 5.1 复合类型: 元组 ((Type, Type, Type)一旦声明, 其长度不会增大或缩小,元组中的每一个位置都有一个类型, 且这些不同值的类型也不必是相同的, 通过 tuple.index 访问)
let tup: (i32, u32, char) = (-1314, 2233, 'a'); // 显式的指定类型
let tup = (-1314, 2233, 'a'); // 让 rust 自动推导
tup.1; // 通过 . 下标访问元组元素
// 5.1.1 元组的解构赋值
let (a, b, c) = tup;
println!("a: {}, b: {}, c: {}", a, b, c);
// 5.2 复合类型: 数组 ([Type; Size], 数组是一整块分配在栈上的内存, 一旦声明, 其长度不会增大或缩小, 数组中的每个元素的类型必须相同, 可以通过 array[index] 访问)
let arr: [char; 5] = ['a', 'b', 'c', 'd', 'e']; // 显式指定类型
let arr = ['a', 'b', 'c', 'd', 'e']; // 让 rust 自动推导
let arr = [0; 5]; // 创建一个长度为5每个元素都是0的数组
// 5.2.1 数组的 Size 也是数组类型的一部分
fn show(_arr:[char; 3]) { // 接收一个长度为 3 的数组
for i in &_arr {
println!("{}", i)
}
}
// show(arr) // expected an array with a fixed size of 3 elements, found one with 5 elements
// 5.2.2 无效的数组元素访问
println!("arr[0]: {}", arr[0]);
println!("arr[5]: {}", arr[5]);
// 6. Range 类型
let r = 1..2;
}
fn stu3() {
println!("stu3: 函数");
// 函数中的参数等价于一个隐式的let绑定,而let绑定本身是一个模式匹配的行为
// 1. 函数的调用及定义
say_hello();
fn say_hello() {
println!("hello")
};
// 2. 函数的参数(函数在定义时, 必须声明函数的类型)
fn say_info(name: &str) {
println!("我叫 '{}'", name)
}
say_info("小明");
// 3. 包含语句和表达式的函数体 (下面是一个代码块, 他的值是6, 注意这里 x + 1 是没有分号的, 表示这是一个表达式, 如果加上分号 ';' ,则变成了一个语句,不会再有返回值返回到变量y上)
let y = {
let x = 5;
x + 1
};
println!("{}", y);
// 4. 函数的返回值 (在rust中, 如果有返回值,函数需要使用箭头指定函数的返回类型, 如果语句中没有分号结尾, 则最后一行表达式会被当做该函数的返回值)
fn sum(a: i32, b: i32) -> i32{
a + b
// return a + b
}
println!("10 + 5 = {}", sum(10, 5));
}
fn stu4() {
println!("stu4: 控制流");
// 1. 分支判断(rust, 分支判断只能使用 bool 值, 这和其他语言是不同的, 比如其他语言任何非 0 的值都为true, 在rust中则必须为 bool 值)
let a: u32 = 3;
if a > 5 {
println!("a 小于 5, a 是: {}", a)
}else if a == 5 {
println!("a 等于 5")
}else {
println!("a 小于 5, a 是: {}", a)
}
// 2. 值判断
println!("3 < 5: {}", 3 < 5);
println!("3 > 5: {}", 3 > 5);
println!("3 <= 5: {}", 3 <= 5);
println!("3 >= 5: {}", 3 >= 5);
println!("3 == 5: {}", 3 == 5);
println!("3 != 5: {}", 3 != 5);
// 3. 三目运算 (rust 里并没有提供类似语法, 但是赋值语句可以使用表达式, 注意! if else中的返回值必须是相同类型)
let _flag: bool = true;
let a = if _flag { 5 }else { 10 };
println!("使用表达式来进行赋值操作 a: {}", a);
// 4. loop 无条件循环相当于 while true
let mut counter = 0;
loop {
counter += 1;
if counter == 100 {
break
}
if counter % 10 == 0 {
continue
} else if counter % 2 == 0 {
println!("in loop: {}", counter);
}
};
// 循环体表达式返回 (通过 break 关键字, 使循环停止,并把后面的表达式执行结果返回)
let num_100 = loop {
counter += 1;
if counter == 200 {
break counter * counter
}
};
println!("通过 loop 表达式赋值, num_100: {}", num_100);
// 5. while 循环: while true循环块返回的是单元值 ()
let mut counter = 0;
while counter < 100 {
counter += 1;
if counter % 2 == 0 {
println!("in while: {}", counter);
continue
}
}
// while 循环内 是返回单元值, 这里虽然按照逻辑返回的是123, 但是编译器认为while可真可假,所以循环体内的表达式会被忽略
// let while_type = || { // Error: | |_____^ expected integer, found `()`
// while true {
// return 123;
// };
// };
// 6. for 循环
let arr: [char; 3] = ['a', 'b', 'c'];
// for i in &arr{
for i in arr.iter(){
println!("in for: {}", i)
}
// 6. 使用 Range 进行 for 循环
for num in (1..4){
println!("in for range: {}", num)
}
}
fn stu5() {
println!("stu5: 所有权");
// Rust里的赋值与其他语言比起来有点特别,默认是一个移动操作
// 栈中的所有数据都必须占用已知且固定的大小, 指针的大小是已知并且固定的, 你可以将指针存储在栈上, 不过当需要实际数据时, 必须访问指针
// 堆中存放大小未知或大小可能变化的数据, 访问堆上的数据比访问栈上的数据慢, 因为必须通过保存在栈上指针来访问
// Rust 中的每一个值都有一个被称为其 所有者(owner)的变量
// 值在任一时刻有且只有一个所有者
// 当所有者(变量)离开作用域,这个值将被丢弃 执行 drop 相关
// !! 注意, 不可变引用 类型的变量会自动实现Copy, 例子9, 而可变引用是没有实现Copy的
// 1. 变量作用域 (一个花括号一个作用域, 外部作用域是无法访问到局部变量的)
let a: i32 = 1;
{
let b: i32 = 2;
println!("a in scope: {}", a);
println!("s in scope: {}", b)
}
// println!("{}", s) // error: not found in this scope
// 2. String 类型与堆 (String有三部分组成: 1.一个指向堆中存放具体内容内存的指针, 2. 长度: 当前使用了多少字节的内存, 3. 容量: 从操作系统总共获取了多少字节的内存, 具体的内容分配在堆上的, 而字面量是因为字面量在编译时就知道了内容,提前分配了内存所以直接硬编码进了可执行文件, 对于String类型,为了支持一个可变的文本片段,需要在运行时向操作系统请求内存,并将指针分配到栈上)
let mut s = String::from("hello"); // 当调用 String::from 时, 请求起所需内存, 当String变量离开作用域的时候会调用 drop 方法清理内存
s.push_str(" world");
println!("String pushed: {}", s);
// let mut s = "abc"; // 创建一个字面量变量
// s. // 由字面量创建的变量, 很多修改方法都是没有的
// 3. 变量与数据交互的方式: 移动 (对保存堆中具体数据的指针的栈数据进行复制的时候, 注意 String类型被移动的时候,只是从栈上拷贝了原本的指针 长度 和容量,但是有一个问题两个变量的中保存的指针都指向了同一个位置,由于String类型在离开作用域的时候回自动执行 drop 方法清理内存,则这两个变量都会尝试释放相同的内存,这就引发了 '二次释放' 的问题,导致内存污染, 所以 rust 在进行这种移动操作类似浅拷贝完成的时候,会同时使第一个变量失效,只剩下最新的变量有效)
let x = String::from("hi");
let y = x;
// println!("移动String x: {}", x); // error: borrow of moved value: `x`
println!("移动String y: {}", y);
// 3.1 变量与数据交互的方式: 拷贝 (对存放栈中的数据是一般来说直接拷贝, 不会使变量失效, 原因是像整型这样的在编译时已知大小的类型被整个存储在栈上,所以拷贝其实际的值是快速的, 没有理由在创建变量 y 后使 x 无效)
let x = 2;
let y = x;
println!("移动i32 x: {}", x);
println!("移动i32 y: {}", y);
// 4. clone 深拷贝(使用clone 使存放 保存数据的堆 的指针 的栈中的数据 进行复制后, 依然有效,其原理是堆中的数据复制一份到新内存中并让新变量保存的指针指向该内存地址)
let x = String::from("say");
let y = x.clone();
println!("Clone String x: {}", x);
// 5. 可Copy的类型(如果一个类型拥有 Copy trait,一个旧的变量在将其赋值给其他变量后仍然可用)
// Copy trait继承自Clone trait,意味着,要实现Copy trait的类型,必须实现Clone trait中定义的方法
// 所有整数类型,比如 u32
// 布尔类型,bool, 它的值是 true 和 false
// 所有浮点数类型,比如 f64
// 字符类型,char
// 元组,当且仅当其包含的类型也都是 Copy 的时候;比如,(i32, i32) 是 Copy 的,但 (i32, String) 就不是
// 6. 所有权与函数 (将值传递给函数在语义上与给变量赋值相似, 向函数传值也会发生移动,或者复制, 就像赋值语句 let x = y 一样
fn say_name(name:String){
println!("hi i am {}", name) // 这里形参离开作用域,并调用 drop 方法
}
fn say_age(age:u32){
println!("hi, i age is {}", age)
}
let x = String::from("abc"); // x 变量进入作用域
let y = 123; // y 变量进入作用域
say_name(x); // x 的值移动到函数中, 之后函数的形参接收该值, 但是!由于形参接收该值之后,在函数执行完毕之后,内部的作用域会执行drop方法,包括形参, 所以下面已经不再有效
// println!("x: {}", x); // error: borrow of moved value: `x`
say_age(y); // y 应该移动到函数中, 但是 u32 是 Copy 的,所以后面可以继续用 y
println!("y: {}", y);
// 7. 所有权与返回值
fn f1()->String{
let x = String::from("abc"); // x 进入作用域
x // 返回 x 并移出给调用的函数
}
fn f2(s:String)->String{
s // 返回 s 并移出给调用的函数
}
let s1 = f1(); // f1 将返回值移给 s1
let s2 = String::from("ohh"); // s2进入作用域
let s3 = f2(s2); // s2 被移动到 f2 中, 但 f2 也将它移动到了 s3中, 所以 "ohh" 现在的所有权在 s3 中
// 8. 解引用操作会获得所有权(可能会造成 "野指针")
let s = String::from("hello");
let s1 = &s;
println!("s: {}", s);
// let s2 = *s1; // 如果这里解引用, s2获得了s的所有权, s1是 &s 就变成了 "野指针"
// 9. 引用会实现Copy 特性
let a = "hello".to_string();
let b = &a;
let x = b; // 这里发生copy 注意这里 x和b 是同一个生命周期
let f = || b; // 这里发生copy
drop(b);
f(); // 可见上面drop掉, 是不会影响闭包内的变量的
// 不可变引用变量进行赋值操作, 会发生所有权转移
let s = "hello".to_string();
let s1 = &s;
let s2 = s1; // s1 copy 到 s2
println!("s1: {}, s2: {}", s1, s2);
// 可变引用类型是没有Copy在赋值操作会移动引用变量 (写出变量注解,即变成 reborrow)
let mut s = "hello".to_string();
let s1 = &mut s;
let s2:&mut String = s1; // s1 所有权 move 到 s2, 可变引用赋值操作需要使用变量注解变成 reborrow
s2; // s2 生命周期到此结束,所有权返回到本身的s1
s1;
}
fn stu6() {
println!("stu6: 引用");
// 共享不可变,可变不共享
// 引用类型的 所有权返回/回归: 可变引用(借用)的变量,会发生所有权转移,在这个可变引用 drop 后,所有权还是会回到原来的变量上去; 因为只能有一个可变引用, 所以这里rust 使用的所有权转移保证只存在一个可变引用
// 冻结: 数据被不可变地引用时,它还会冻结(freeze);已冻结的数据无法通过原始 对象来修改或者转移所有权,直到对这些数据的所有引用生命周期结束为止,保证引用数据的准确
// !! 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用, 在不可变引用失效之前, 不能创建可变引用
// 引用必须总是有效的
// 引用的作用域/生命周期; 是从声明的地方开始 -> 一直到最后一次使用结束
// 在不可变借用生命周期期间,所有者不能修改资源,并且也不能再进行可变借用; 在可变借用期间,所有者不能访问资源,并且也不能再出借所有权
// 解引用操作 可能会 因为后续的操作而可能被消耗, 获得所有权(可能会造成 "野指针", 所以对于未实现COPY trait的类型解引用可能会发生move),如果一个没有实现Copy trait的对象 通过引用 解引用, 那因为没有实现Copy,所以会发生move, 如果发生了 move, 那么原本的引用就是"野指针"
// 1. 以一个对象的引用(是指向对象的地址, 指针)作为参数而不是获取值的所有权, 这样在传入函数参数的时候由于传入的是该变量的指针, 因为并不拥有这个值, 当引用离开作用域时其指向的值也不会被丢弃
fn get_len(s:&String)->usize{
s.len()
}
fn push_str(s:& mut String){
s.push_str(" world");
}
let mut s1 = String::from("hello"); // 创建一个变量
let s = &s1; // 创建一个引用
println!("&s1: {}", s);
let ms = &mut s1; // 创建一个可变引用(之前的引用都无法使用失效,不能在拥有不可变引用的同时拥有可变引用)
println!("&mut s1: {}", ms);
// println!("&s1: {}", s); error 无法之前的引用,因为都已经无法使用
push_str(ms); // 通过函数直接操作指针指向的数据
let l = get_len(&s1);
println!("修改后 '{}' 的长度为: {}", s1, l);
// 2. 不能在拥有不可变引用的同时拥有可变引用, 可以同时存在, 但不能同时存活(生命周期发生交叉)
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
let r3 = &mut s; // 没问题 同时存在,
// println!("{}, {}, and {}", r1, r2, r3); // 上面会error mutable borrow occurs here 不允许使用, 可变引用和不可变引用同时存活, 生命周期发生了交叉, 如果使用则上方会出错
// 2.1 一个引用的作用域从声明的地方开始一直持续到最后一次使用为止;例如,因为最后一次使用不可变引用在声明可变引用之前, 不可变引用 r1 和 r2 的作用域在 println! 最后一次使用之后结束
let mut s = String::from("say");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
println!("{} and {}", r1, r2);
// 此位置之后 r1 和 r2 不再使用下方可以借用可变引用
let r3 = &mut s; // 没问题
println!("{}", r3);
// 3. 垂悬引用
// let reference_to_nothing = dangle();
// fn dangle() -> &String { // dangle 返回一个字符串的引用
// let s = String::from("hello"); // 创建一个字符串
// &s // 返回字符串 s 的引用
// } // 这里 s 离开作用域并被丢弃;其内存被释放, 所以上面函数返回的是一个空的引用
// 4. 所有权返回
let mut s1 = String::from("hi");
let s2 = &mut s1;
println!("s2: {}", s2); // 后面没有 s2 的使用, 此时s2的生命周期结束, 被销毁掉了
println!("s1: {}", s1); // 没问题, s2 销毁掉之后, 这里 println 使用的 s1的可变引用
// 下面是错误代码以及说明
// println!("s1: {}", s1); // 这里 println 默认使用了 s1 的不可变引用, 但是因为 后续仍然有 s2 的使用, 所以 s2 这个可变引用 并没有被 销毁, 违反了 可变引用和不可变引用 同时存在的规则
// println!("s2: {}", s2);
// 冻结 已经被借用对象, 在引用未结束前, 无法再次分配新值,
let mut b = 1;
{
let c = &b;
// b = 2; // error: assignment to borrowed `b` occurs here, 因为 b 的引用, 在下面还在使用,所以引用还没结束,这里无法修改分配
println!("c: {}",c);
}
// 解引用操作会获得未实现 Copy trait 的对象的所有权(会造成 "野指针")
let s = String::from("hello");
let s1 = &s;
println!("s: {}", s);
// let s2 = *s1; // 如果这里解引用, s2获得了s的所有权, s1是 &s, s1 就变成了 "野指针", 这是编译器禁止的
// 在引用期间,所有者不再出借所有权 (所以对于未实现COPY trait的类型是无法解引用的)
struct F1;
impl F1{
fn func(self){}
}
struct F2{fs:Vec<F1>};
impl Drop for F2{
fn drop(&mut self){
for f in &mut self.fs{
// f.func(); // 这里自动解引用相当于 调用了 (*f).func(), 因为上方存在可变引用, 而func方法使用的是self会转移所有权, 所以这里无法解引用的
}
}
}
// 对于未实现COPY trait的类型解引用会获取其所有权
let mut s = "hello".to_string();
let s1 = &s;
// let s2 = *s1;
println!("{}",s1);
// fn say1(name:&String){};
// fn say2(name:String){};
// let s1 = String::from("小明");
// let s2 = String::from("小花");
// say1(&s1);
// say2(s2);
// println!("s1: {}", s1);
// // println!("s2: {}", s2) // error: borrow of moved value: `s2`
//
// 使用引用进行复制
// let mut x = String::from("abc");
// let mut y = &x;
// println!("使用引用传值 x: {}", x);
// println!("使用引用传值 y: {}", y);
// x.push_str(" xyz");
// println!("使用引用传值 y: {}", y);
}
fn stu7() {
println!("stu7: slices");
let s1 = String::from("hello,world");
println!("s1: {}", s1);
let s1_bytes = s1.as_bytes();
for (idx, &data) in s1_bytes.iter().enumerate() {
println!("idx: {}, data: {}", idx, data)
}
for i in s1_bytes.iter(){
println!("{}", i)
};
// 使用切片来获得字符串的部分引用
println!("&s1[0..5]: {}", &s1[0..5]); // 看左不看右
println!("&s1[1..=5]: {}", &s1[1..=5]); // 看左同时看右
println!("&s1[..=5]: {}", &s1[..=5]); // 从头开始切片
println!("&s1[1..]: {}", &s1[1..]); // 切片到尾部
println!("&s1[..]: {}", &s1[..]); // 默认全部
// 注意 中文字符的步长为 3 目前这里处理 utf8 字符会有一些问题
let s2 = String::from("你好");
// println!("&s2[..]: {}", &s2[0..2]); error
println!("&s2[..]: {}", &s2[0..3]);
println!("&s2[..]: {}", &s2[..]);
// 字面值就是slice
let s3 = "我很好";
// 其他类型的slice
let arr1:[i32; 4] = [1,2,3,4];
let slice_arr1 = &arr1[0..2];
println!("&arr1[..]: {} {}, len: {}", slice_arr1[0], slice_arr1[1], slice_arr1.len());
}
fn stu8() {
println!("stu8: 结构体");
// 单元结构体实例就是其本身 struct S; let s1 = S; let s2 = S; 在Debug模式下 s1 和 s2是不同的内存地址, 在release编译模式下,他们进行了优化是相同的内存地址
// 全范围(..)的 RangeFull,就是一个单元结构体
// 1. 定义一个结构体
struct Student {
name: String,
age:u32,
}
// 2. 创建一个结构体实例
let stu1 = Student{
name:String::from("小明"),
age:12
};
// 3. 修改结构体字段 需要添加 mut 关键字
let mut stu2 = Student {
name: String::from("小花"),
age:18
};
stu2.age = 17;
println!("学生2 的年龄为 {}", stu2.age);
// 4. 参数名字和字段名字同名的快速创建对象的方法
fn create_stu(name:String, age:u32) -> Student {
Student{
name,
age
}
}
let stu3 = create_stu(String::from("小亮"),28);
println!("学生3 的名字为 {}", stu3.name);
// 5. 从其他的结构体创建实例(使用 .. 指定了剩余未显式设置值的字段应有与给定实例对应字段相同的值)
let stu4 = Student {
..stu3 // 注意,因为所有权的原因, stu3.name 被解构出来了,在离开作用域时已经被删除了
};
// println!("学生3 的名字为 {}", stu3.name); // error 和上面呼应
println!("学生4 的信息除了名字, 其他都是来自 学生3 的, 名字为 {}, 年龄为: {}", stu4.name, stu4.age);
// 6. 元组结构体(字段没有名字 通过下标访问, 每一个一个结构体有其自己的类型,即使结构体中的字段有着相同的类型)
struct Point(i32, i32);
struct Color(i32, i32);
let a = Point(10, 20);
println!("a.0: {}, a.1: {}", a.0, a.1);
// 7. 没有任何字段的结构体(类单元结构体)
struct Dog{};
// 8. 打印结构体的所有信息
#[derive(Debug)]
struct Teacher {
name:String,
age:u32
}
let t1 = Teacher{
name:String::from("张三"),
age:29
};
println!("t1的所有信息: {:?}", t1);
println!("t1的所有信息: {:#?}", t1); // 自动换行打印
// 9. 结构体中的方法
#[derive(Debug)]
struct People { // 创建一个结构体
name:String,
};
impl People { // 实现结构体的方法
fn get_name(&self)->&String{ // 传入实例的引用
&self.name // 这里返回 String 的引用, 防止 String 被清理
}
fn eat(&self){
println!("我在吃饭");
}
};
let p1 = People{
name:String::from("李四")
};
p1.eat();
println!("我叫: {}", p1.get_name());
println!("p1 所有的信息: {:#?}", p1);
// 10. 关联函数 允许在 impl 块中定义 不 以 self 作为参数的函数, 因为没有 self 所以这里它们仍是函数 而不是方法,,因为它们并不作用于一个结构体的实例
struct User{
name:String
}
impl User {
fn create(name:String) ->User {
User{
name
}
}
}
let user1 = User::create(String::from("小亮"));
println!("通过关联函数使用结构体名和 :: 语法来调用这个关联函数, 用户1的名字为: {}", user1.name);
}
fn stu9() {
println!("stu9: 枚举类型");
// 注意枚举本质属于 函数指针 类型( 例子 3 )
// 1. 类似c语言定义枚举类型
enum Ip {
V4,
V6
}
struct IpAddr {
ip_type:Ip,
ip_addr:String
}
let i1 = IpAddr {
ip_type: Ip::V4,
ip_addr: String::from("127.0.0.1")
};
// 2. rust语言推荐方法
#[derive(Debug)]
enum IpAddr2{
V4(String)
};
let i1 = IpAddr2::V4(String::from("127.0.0.1"));
let i2 = IpAddr2::V4(String::from("0.0.0.0"));
// 3. !! 枚举本质类似函数指针 如下代码推导 可见 function 和 枚举 在签名上没差, IpAddr2::V4 是函数 fn(String) -> IpAddr2 的指针
fn ip_func(s:String) -> IpAddr2{ IpAddr2::V4(s)}
let i4 = ip_func;
let i3:fn(String) -> IpAddr2 = IpAddr2::V4;
// 使用 i3 创建一个枚举类型,打印
let i4 = i3("hello".to_string());
dbg!(i4);
// 3. 可以是不同的类型
#[derive(Debug)]
enum IpAddr3{
V4(u32, u32, u32, u32),
V6(u32, u32, u32, u32)
};
let i1 = IpAddr3::V4(127, 0, 0, 1);
println!("{:?}",i1);
// 4. 经典用法, 其每个成员都储存了不同数量和类型的值
#[derive(Debug)]
enum Message {
Quit, // 没有关联任何数据 相当于 struct Quit;
Move{x: i32, y:i32}, // 包含一个匿名结构体 相当于 struct Move {x:i32, y:i32}
Write(String), // 包含单独一个String 相当于 struct Write(String)
Change(i32, i32, i32) // 包含三个i32 相当于 struct Change(i32, i32, i32)
};
// 5 枚举类型的方法( 注意使用match时, 需要给枚举的情况都包括, 如果懒得匹配可以使用 _ 来进行通用匹配 )
impl Message {
fn call(&self){
match &self {
Message::Quit => println!("this is Quit"),
Message::Move{x,y} => println!("this is Move"),
// Message::Write(&String) => println!("this is Write"), error; expected struct `std::string::String`, found reference
Message::Change(a, b, c) => println!("this is Change a: {}, b: {}, c: {}, &self: {:?}", a, b, c, &self),
_ => println!("上面没有被匹配的走到了这里: {:?}", &self)
}
}
};
let cm = Message::Change(1,2,3); // 创建了一个拥有类型 Change(1, 2, 3) 的变量 m, 这就是当 m.call() 运行时 call 方法中的 self 的值
cm.call();
let wm = Message::Write(String::from("h"));
wm.call();
// 6. Option 枚举类型(是标准库定义的一个枚举)
// enum Option<T>{
// Some(T),
// None
// }
// 6.1 定义 Option类型
let some_number = Some(5);
let some_string = Some(String::from("hi"));
let none:Option<i32> = None;
println!("some_number: {:?}", some_number);
println!("some_string: {:?}", some_string);
println!("none: {:?}", none);
// 6.2 Option<i32> 无法和 i32 相加
let x:i32 = 5;
let y:Option<i32> = Some(3);
// x + y; // error: no implementation for `i32 + std::option::Option<{integer}>`
// 6.3 Option 类型的使用(使用 match 对 Option 类型匹配的时候, 需要把内部的类型全部匹配上)
let mut temp: i32 = 0;
match y {
Some(i) => {println!("this is Some: {}", i); temp = i;}
None => println!("this is None")
};
println!("x + y(temp) = {}", x + temp);
// 6.4 Option类型 函数中使用
fn f1(x:Option<i32>) -> Option<i32> {
println!("f1 函数接收到参数: {:?}",x);
match x {
None => None,
Some(_x) => {println!("匹配到Some: {}, 为其增加 1", _x); Some(_x + 1)},
}
}
let y = Some(10);
println!("经过f1函数为 Option 类型增加 1 后为: {:?}", f1(y));
// 7. if let 的使用(可以不使用 match 匹配, 来处理只匹配一个模式的值而忽略其他模式的情况, 使用 = 分隔的一个模式和一个表达式)
if let Some(v) = y {
println!("if let {}", v)
} else {
println!("None")
}
// 7.1.1 使用 match匹配 Some中值为3的情况
let some_u8_value = Some(3);
match some_u8_value {
Some(3) => println!("match, three"),
_ => (),
}
// 7.1.2 使用if let匹配 Some中值为3的情况
if let Some(3) = some_u8_value{
println!("if let, three")
}
// 7.1.3 可以在if let 后包含一个 else
if let Some(999) = some_u8_value{
println!("if let, 999")
} else {
println!("没找到")
}
// 8. 如果一个函数的参数可能为,或者返回可能为空 我们可以使用Option来传参
fn say(name:&str,age:Option<i32>) -> Option<i32>{
match age {
Some(a)=> {
println!("我叫: {}, 我今年 {} 岁了", name, a);
return Some(a)
}
None => {
println!("我叫: {}, 我也不知道今年多大了", name);
return None
}
};
};
say("小亮",Some(12));
say("小花", None);
}
fn stu10() {
println!("stu10: vector类型: 线性序列");
// 线性序列:向量, 也是一种数组,和基本数据类型中的数组的区别在于,向量可动态增长
// 1. 创建空的 vector Vec<T>
let mut v:Vec<u32> = Vec::new();
v.push(1);
// 2. 创建包含初始值的 vector vec![...]
let v = vec![1,2,3];
// 3. 丢弃 vector
{
let v = vec![2,3,4];
}
// 4. vector读取元素 (同 array 读取一样, 但是多了一个get方法传入index,返回Option<&T>, 是rust推荐的方法,因为索引越界不会错误,会被None捕捉)
let v = vec!['a', 'b', 'c'];
let v_1:&char = &v[1];
let v_2:char = v[1];
println!("v_1: {}", v_1);
println!("v_2: {}", v_2);
let v_3:Option<&char> = v.get(2);
println!("v_3: {:?}", v_3);
match v.get(1) {
Some('b') => println!("通过 match 匹配 b"),
Some('c') => println!("通过 match 匹配 c"),
None => println!("没有找到"),
_ =>()
}
match v.get(999) {
Some('b') => println!("通过 match 匹配 b"),
Some('c') => println!("通过 match 匹配 c"),
None => println!("没有找到"),
_ =>()
}
// 5. 更新元素
let mut v2:Vec<i32> = Vec::new();
v2.push(1);
v2.push(2);
v2.push(3);
println!("经过了3次push: {:?}", v2);
// 6. 不可变的遍历
for i in &v2 {
println!("不可变遍历 值为: {}", i)
}
// 7. 可变的遍历
for i in & mut v2{
*i += 1
}
println!("经过了可变的遍历: {:?}", v2);
// 8. vector 通过枚举 使用不同类型
enum Context{
Text(String),
Float(f64),
Int(i32)
};
let v1 = vec![
Context::Text(String::from("hi")),
Context::Float(0.618),
Context::Int(64),
];
// 9. 补充(在拥有 vector 中项的引用的同时向其增加一个元素, 之前的不可变引用是无法再使用了,这是因为:在 vector 的结尾增加新元素时, 在没有足够空间 将所有所有元素依次相邻存放的情况下, 可能会要求分配新内存并将老的元素拷贝到新的空间中,这时,第一个元素的引用就指向了被释放的内存, 借用规则阻止程序陷入这种状况)
let mut v3 = vec![1, 2, 3, 4, 5];
let first = &v3[0]; // 1. 读取了一个不可变引用
v3.push(9); // 2. vector 修改了下标为0的值 这里其实使用了 可变引用, 在使用了可变引用之后, 之前的所有不可变引用都无法使用
// println!("first: {}", first) // 3. 这里 无法 使用了 first 的不可变引用
}
fn stu11() {
println!("stu11: String类型");
// 1. 创建一个空的字符串
let mut s1:String = String::new();
s1.push_str("S1");
println!("s1: {}", s1);
// 2. 通过字面值创建一个 String
// 2.1 使用 String::from()
let s2_1: String = String::from("S2_1");
println!("s2_1: {}", s2_1);
// 2.3 使用 str 的方式
let s2:String = "S2".to_string();
println!("s2: {}", s2);
// 3. 更新 String
let mut s3:String = String::new();
// 3.1 pust_str(需要传入 &str, 这里传入push_str的参数是字符串的 slice,所有权并没有转移)
let s = "push_str".to_string();
s3.push_str(&s);
s3.push_str("push_str");
// 3.2 push push只能传入 char 字符
s3.push('p');
s3.push('u');
s3.push('s');
s3.push('h');
println!("经过了 一些列的更新后的值为: {}", s3);
// 3.3 使用 "+" 合并字符串(注意 使用了 + 之后 所有权会转移, 就相当于获取s1 的所有权,附加上从 s2 中拷贝的内容,并返回结果的所有权, 类似 fn add(self, s: &str) -> String { ,这里self没有使用&self 所以所有权会被转移)
let s3_1 = String::from("hello");
let s3_2 = String::from("world");
let s3_3 = s3_1 + &s3_2; // 这里 s3_1 的所有权已经被转移了
println!("使用了 + 进行字符串合并: {}", s3_3);
// println!("s3_1: {}", s3_1); error: value borrowed here after move
// 4. String 索引
println!("通过字符串下标获得, s3: {}",&s1);
// 5. str 索引(由于rust中使用utf8, 一个中文占3个字节,或者其他数量字节, 所以rust不推荐使用索引来访问字符串元素,因为 Rust 不得不检查从字符串的开头到索引位置的内容来确定这里有多少有效的字符;, 可以使用slice, 注意slice 越界问题,)
let s = String::from("你好");
println!("通过字符串slice访问字符串: {}", &s[..3]); // 你
// println!("通过字符串slice访问字符串: {}", &s[..4]); // error: thread 'main' panicked at 'byte index 4 is not a char boundary; it is inside '好' (bytes 3..6) of `你好`'
// 6. 遍历
// 6.1 chars (得到字符串的字符)
for char in s.chars() {
println!("s.chars: {}", char)
}
// 6.2 bytes (返回每一个原始字节)
for byte in s.bytes() {
println!("s.bytes: {}", byte)
}
for byte in s.as_bytes() {
println!("s.as_bytes: {}", byte)
}
// 7. format! 格式化字符串( 由于是一个宏, 所以所有权不会转移)
let s1 = String::from("hello");
let s2 = "world";
let s3 = format!("{}, {}", s1, s2);
println!("format!: {}", s3)
}
fn stu12() {
println!("stu12: HashMap");
// HashMap是无序的,BTreeMap是有序的
// Key必须是可哈希的类型,Value必须是在编译期已知大小的类型
// 1. 新建一个HashMap, 数据存放在堆中
let mut h: HashMap<&str, i32> = HashMap::new();
h.insert("小亮", 18);
h.insert("小花", 20);
println!("新建了一个HashMap: {:?}", h);
// 1.1 使用vector 的 collect 方法, 对于当时无法确定的类型可以使用 _ 占位, 让rust推导
let names = vec!["小强", "小李"];
let ages = vec![22, 17];
println!("names.iter().zip(ages.iter()): {:?}", names.iter().zip(ages.iter()));
let h2: HashMap<_, _> = names.iter().zip(ages.iter()).collect();
println!("h2: {:?}", h2);
// 2. HashMap和所有权(键值对被插入后就为哈希 map 所拥有)
let n:String = String::from("张三");
let mut h: HashMap<String, i32> = HashMap::new();
h.insert(n, 19);
// println!("n: {}", n) // error: value borrowed here after move
// 3. 访问HashMap中的值 使用get(key)获取的是Option<T>, 或者使用遍历的方式无顺序的获取
println!("通过get获取-> 张三的年龄为: {:?}", h.get(&String::from("张三")));
for (k,v) in h {
println!("通过循环遍历获取-> {} : {}", k, v)
}
// 4. 更新HashMap
// 4.1 覆盖一个值
let mut h = HashMap::new();
h.insert("李四", 20);
println!("覆盖前: {:?}", h);
h.insert("李四", 22);
println!("覆盖后: {:?}", h);
// 4.2 只在键没有对应值时插入 使用 entry or_insert, Entry 的 or_insert 方法在键对应的值存在时就返回这个值的Entry可变引用( &mut V ), 如果不存在则将参数作为新值插入并返回修改过的 Entry可变引用( &mut V )
h.insert("小花", 19);
let r = h.entry("李四").or_insert(25);
println!("Entry 的 or_insert方法返回的值: {}", r);
println!("使用entry on_insert 插入: {:?}", h);
// 4.3 根据旧值更新一个值 or_insert 方法事实上会返回这个键的值 的一个可变引用( &mut V );这里我们将这个可变引用储存在 count 变量中,所以为了赋值必 须首先使用星号( * )解引用 count
let s = "hello, world";
let mut h: HashMap<char, i32> = HashMap::new();
for i in s.chars() {
let count = h.entry(i).or_insert(0);
*count += 1
}
println!("统计结果: {:?}", h)
// 5. Hash函数
}
fn stu13() {
println!("stu13: 模块");
// 1. 定义并使用一个公开的模块(模块内部都是私有的)
handle1::dog::eat();
// 2. 使用外部lib库 先在项目目录 使用 "cargo new --lib mylib" 创建lib库项目, 然后添加rs模块文件, 在lib.rs中需要向外暴露模块, 注意需要修改 Cargo.toml 文件增加路径
mylib::utils::people::say_hello(); // 使用绝对路径调用外部库
// use mylib::utils::people::*; // 导入该模块下全部
use mylib::utils::people::say_hello as say; // 使用别名
say(); // 使用相对路径调取
// 3. 使用外部库的结构体
use mylib::utils::students::Student;
let stu1 = Student::new(String::from("小明"),18);
stu1.get_info();
stu1.name; // 公开的属性外部可以直接获取
// stu1.age; // 无法获取没有公开的属性 error: field `age` of struct `mylib::utils::students::Student` is private
// 4. 子模块如果想调用父模块的方法, 可以使用在模块汇总 super::function()
use mylib::utils::print_a::print_b;
print_b::print_b_to_a();
// 5. 调用外部网路上的库 来使用sha256加密(如果下载慢,可以百度切换源)
extern crate crypto;
use crypto::sha3::Sha3;
let mut hasher = Sha3::sha3_256();
hasher.input_str("hello, world");
let ret = hasher.result_str();
println!("{:?}", ret)
}
fn stu14() {
println!("stu14: 异常");
// rust 中错误分为两个类别, 可恢复错误/不可恢复错误
// 可恢复错误: 通常表示错误和重试操作是合理的情况, 例如没找到文件, rust中使用Result<T, E> 来实现
// 不可恢复错误: 通常是bug引起的, rust中通过panic!这个宏实现
//1. 使用 panic! 手动引发异常
// panic!("出错了")
// 2. Result
// enum Result<T, E>{ // T代表成功时返回的Ok成员中的数据类型, E代表失败Err成员中的数据类型, Result通过组合有效输出的数据类型和错误的数据类型来使用类型;例如,如果有效输出的数据类型为u64且错误类型为String ,则返回类型应为Result<u64, String>
// Ok(T),
// Err(E)
// }
// use std::fs::File;
// let f = File::open("../1.txt");
// let r = match f {
// Ok(file) => file,
// Err(e) => panic!("panic!->error: {:?}",e),
// };
// Result 简写方式-1 快速获得结果 unwrap, 如果Option类型具有Some值或Result类型具有Ok值,则其中的值将传递到下一步, 如果是Err则为我们调用panic!
// let f = File::open("../1.txt").unwrap();
// println!("简写方式-1, {:?}",f);
// Result 简写方式-2 自定义异常信息
// let f = File::open("../1.txt").expect("出错了");
// println!("简写方式-2, {:?}",f);
// 3. 传播错误
// fn open_file() -> Result<String, io::Error>{ // 如果没有任何错误 函数的调用者会收到一个包含 String 的 Ok 值 ,如果函数遇到任何错误函数的调用者会收到一个 Err 值,它储存了一个包含更多这个问题相关信息的 io::Error 实例
// let f = File::open("../1.txt");
// let mut f = match f {
// Ok(file) => file, // 读取成功返回给 f
// Err(e) => {
// return Err(e); // 如果出错直接结束函数运行
// }
// };
// println!("处理中");
// let mut s = String::from("");
//
// let r = match f.read_to_string(&mut s) {
// Ok(_) => Ok(s), // f读取成功之后写入通过引用写入到了 s 中, 所以这里 s 内容是已经写好的了, 直接返回 Ok(s) 到外部即可
// Err(e) => Err(e) // 结果返回 r
// };
// return r
// };
//
// let r = open_file(); // 获得函数中的结果, 是 Ok(s), 或是 Err(s)类型
//
// match r {
// Ok(s) => println!("读取成功: {}",s),
// Err(e) => println!("出错了: {:?}", e)
// };
// 3. 传播错误简写(使用?简写,如果遇到错误 Err 中的值将作为整个函数的返回值, 注意 ? 只能被用于返回值类型为 Result的函数)
fn open_file() -> Result<String,io::Error>{
let mut f = File::open("../1.txt")?;
let mut s = String::new();
// let ret = match f.read_to_string(&mut s) {
// Ok(_) => Ok(s), // 这里返回 Ok(s)即可, 这里的 s 之前已经通过引用传入 f.read_to_string 内了,如果是Ok 那么这里 s 中的内容, 应该是读取的内容
// Err(e) => Err(e)
// };
// return ret // 返回 Ok(s) 或者 Err(e)
// 上面的注释简写为
// f.read_to_string(&mut s)?; // 这一行代码如果出错, 通过 ? 简写异常捕捉则直接返回 Err(e)
// Ok(s)
// 连写的方式
let mut s = String::new();
File::open("../1.txt")?.read_to_string(&mut s)?;Ok(s) // 读取文件 1.txt 如果没读取到直接返回 Err(e) 如果读取到往下走执行 .read_to_string 如果没有运行成功则直接返回Err(e), 如果执行成功因为没带";"号 则最后一句直接返回Ok(s)
}
match open_file() {
Ok(s) => println!("读取成功, 内容为: {}", s),
Err(e) => println!("读取失败, 错误为: {}", e)
}
// Result 类型
// let o: Result<i32, &str> = Ok(8);
// let e: Result<i32, &str> = Err("message");
// println!("o.ok: {:?}", o.ok());
// println!("e.err: {:?}", e.err());
// TODO 目前还不能解决的问题 对整个代码块进行一场捕捉
fn compute(a:i32, b:i32) -> i32 {
a / b
}
compute(1,2);
// compute(1,0);
}
fn stu15() {
println!("stu15: 泛型");
// 1. 泛型要解决的问题: 比如 有一个比大小的函数 需要对 [i32] 和 [char] 类型的array比较大小, 当有几种类型就需要定义几个function
fn max1(li: & [i32]) -> i32{
let mut m = li[0];
for i in li.iter() {
if *i > m {
m = *i
}
};
return m;
}
let mut arr1: [i32;5] = [1, 2, 3, 4, 5];
println!("arr1最大的值为: {}", max1(&arr1));
fn max2(li:&[char]) -> char {
let mut m = li[0];
for i in li.iter(){
if *i > m {
m = *i
}
}
m
}
let arr2 = ['a', 'b', 'c'];
println!("arr2最大的值为: {}", max2(&arr2));
// 2. 定义泛型函数 <> 尖括号中补充 对 泛型的 特征trait 约束; PartialOrd: 表示可以进行数据比较, Copy: 表示T类型具有COPY特征
fn max3<T:PartialOrd + Copy> (li:&[T]) -> T {
let mut m = li[0];
for i in li.iter(){
if *i > m {
m = *i
}
}
return m;
}
let arr3_1 = [1, 2, 3, 4, 5];
let arr3_2 = ['a', 'b', 'c', 'd'];
println!("通过泛型函数, arr3_1最大的值为: {}", max3(&arr3_1));
println!("通过泛型函数, arr3_2最大的值为: {}", max3(&arr3_2));
// 3. 定义泛型结构体
#[derive(Debug)]
struct Point<T>{
x:T,
y:T
};
let p1 = Point{
x:1,
y:2
};
println!("通过泛型结构体, 定义的为: {:?}", p1);
let p2 = Point{
x:2.2,
y:3.4
};
println!("通过泛型结构体, 定义的为: {:#?}", p2);
// 3.1 泛型结构体使用不同类型
#[derive(Debug)]
struct Point2<T, U>{
x:T,
y:U,
};
let p3 = Point2{
x:1,
y:"二"
};
println!("通过泛型结构体使用不同类型: {:?}",p3);
// 4. 枚举中使用泛型
// enum Option<T>{
// Some(T),
// None
// };
// enum Result<T, E>{
// Ok(T),
// Err(E)
// }
// 5. 方法中使用泛型 impl 之后声明泛型, 这样 Rust 就知道Student<T, U>尖括号中的是泛型而不是具体的类型
#[derive(Debug)]
struct Student<T, U>{
name:T,
age:U
}
impl<T,U> Student<T,U>{
fn get_name(&self) -> &T {
&self.name
}
}
let stu1 = Student {
name: "小明",
age:18
};
// 6. 对特征类型实现泛型, 一个没有在 impl之后(的尖括号)声明泛型的例子,这里使用了一个具体类型, 这里就意味着 Point<f64> 的实例 会有一个具体的方法 get_x, 而其他的类型的实例就没有这个方法
struct Point_<T>{
x:T,
y:T
}
impl Point_<f64>{
fn get_x(&self) -> f64 {
self.x
}
}
let p1 = Point_ { x: 1, y: 2 };
let p2 = Point_ { x: 0.5, y: 3.1 };
// p1.get_x(); // error: method not found in `stu15::Point_<{integer}>`
p2.get_x(); // 只有 f64类型的实例才会有方法
// 泛型混合使用
// 使用学生1的name 和学生2 的age 组成一个新的stu3
let stu1 = Student { name: "小亮", age: 12 };
let stu2 = Student { name: "小明", age: 14 };
impl<T,U> Student<T,U>{
fn create<Q, W>(&self, other:Student<Q,W>) -> Student<&T,W>{ // create<Q, W, Y, K, L> 声明使用了泛型类型 Q, W, Y, K, L
&self.name;
Student {
name: &self.name,
age:other.age
}
}
}
let stu1 = Student { name: "小亮", age: 12 };
let stu2 = Student { name: "小明", age: 14 };
let stu3 = stu1.create(stu2);
println!("泛型混合使用创建了stu3的信息: {:#?}",stu3)
}
fn stu16() {
println!("stu16: trait");
// 1. 通过 trait 以抽象的方式定义共享的行为, 用于定义与其他类型共享的功能
// 2. 定义 trait
pub trait GetInfo {
fn get_name(&self) -> &String; // 函数接收一个参数 &self, 返回 &String 类型
fn get_age(&self) -> i32;
}
// 2.1 定义结构体
#[derive(Debug)]
pub struct Student{
name:String,
age:i32
}
pub struct Teacher{
name:String,
age:i32,
subkey:Vec<String>
}
// 3. 实现特征 使用 impl 特征 for 结构体, 对特征中定义的接口函数 在结构体中对函数一一实现
impl GetInfo for Student {
fn get_name(&self) -> &String {
&self.name
}
fn get_age(&self) -> i32 {
self.age
}
};
impl GetInfo for Teacher {
fn get_name(&self) -> &String {
&self.name
}
fn get_age(&self) -> i32 {
self.age
}
};
let stu1 = Student{name:"小亮".to_string(), age:18};
let t1 = Teacher{name:"张三".to_string(), age:28, subkey:vec!["历史".to_string(), "语文".to_string()]};
println!("学生1的名字是: {}, 年龄: {}", stu1.get_name(), stu1.get_age());
println!("老师1的名字是: {}, 年龄: {}, 老师教的课有: {:?}", t1.get_name(), t1.get_age(), t1.subkey);
// 4. 使用特征约束函数参数, trait 作为参数:通过特征, 指定并限制函数的参数,必须拥有某些行为, 如下,print_info必须实现GetInfo这个方法
fn print_info(item:& impl GetInfo){
println!("print_info, name = {}, age = {} ", item.get_name(), item.get_age());
};
print_info(&stu1);
print_info(&t1);
// 5. trait 默认实现: 在特征中实现函数的代码, 作为函数的默认行为,当然也可以进行重载
trait SchoolName {
fn get_school_name(&self) -> String {
"默认-红星小学".to_string()
}
}
// 这里把上面的特征, 绑定到结构体中, 特征中已经默认实现了函数的行为, 所以可以不用再实现相关方法
impl SchoolName for Student {}; // 这里留空即可
impl SchoolName for Teacher {
fn get_school_name(&self) -> String{ // 重写默认实现,自己实现相关方法
"老师覆盖-红星小学".to_string()
}
};
println!("学生学校是: {}",stu1.get_school_name());
println!("老师学校是: {}",t1.get_school_name());
// 6. 使用特征约束函数参数, trait bound 特征语法糖,定义泛型,泛型中使用 <T:特征1 + 特征2> 对函数进行特征约束, 如果使用多个特征进行约束 可以使用 "+" 来链接, 编译器会确保其被限制为那些实现了特定 trait 的类型
pub trait GetName{
fn get_names(&self) -> &String;
}
pub trait GetAge{
fn get_ages(&self) -> i32;
}
impl GetName for Student{ // 普通语法
fn get_names(&self) -> &String{
&self.name
}
}
impl GetAge for Student{ // 普通语法
fn get_ages(&self) -> i32 {
self.age
}
}
// 6.1 使用特征约束 泛型函数, trait bound语法
// 写法1: print_info_1 泛型T, 泛型T 必须实现 GetInfo 这个方法
fn print_info_1<T:GetName + GetAge>(item:&T){
println!("trait bound语法糖实现特征, 写法1 name = {}, age = {} ", item.get_names(), item.get_ages());
};
// 写法2: 使用 where T: 特征1 + 特征2 来对对函数进行约束
fn print_info_2<T>(item:&T)
where T: GetName + GetAge
{
println!("trait bound语法糖实现特征, 写法2 name = {}, age = {} ", item.get_names(), item.get_ages());
}
// 调用
print_info_1(&stu1);
print_info_2(&stu1);
// 7. 使用特征约束函数的返回, 使其返回值必须有相关特征
fn get_item() -> impl GetName + GetAge {
Student{
name:"小花".to_string(),
age:16
}
}
let s = get_item();
println!("get_item限制了函数的返回: name: {}, age:{:?}", s.get_names(), s.get_ages());
// 8. impl 限制函数返回使用特征约束的时候, 无法使用不同的结构体,因为虽然都实现了特征中的方法, 但是不同结构体却是不同类型, 而impl属于静态分发
// fn get_() -> impl GetName{
// if true {
// return Student {
// name: "小李".to_string(),
// age: 12
// }
// }
// return Teacher { // error: expected struct `stu16::Student`, found struct `stu16::Teacher`
// name: "张三".to_string(),
// age: 12,
// subkey:vec!["数学".to_string()]
// }
// }
// 练习
fn max<T>(li: &[T]) -> T
where T: PartialOrd + Copy
{
let mut m = li[0];
for & i in li.iter(){
if i > m{
m = i
}
};
m
};
let lis = ['b', 'c', 'a'];
println!("lis找到最大的数为: {}", max(&lis));
// 9. 使用 trait bound 对结构体的方法进行特征约束 impl <T:约束1 + 约束2, U:约束1 + 约束2> 结构体<T, U>, 只有实现了相应约束的实例, 才会有这个包含在大括号的方法
// 定义特征
trait GetName9{
fn get_name(&self) -> &String;
};
trait GetAge9{
fn get_age(&self) -> i32;
};
// 定义结构体, 这个结构体本身有学生和老师
struct People<T,U> {
master:T,
student:U
}
// 定义结构体的方法, 要求 T和U 需要满足对应的特征, 才会有对用的方法
impl <T:GetInfo, U:GetInfo> People <T, U>{
// impl <T, U> People <T, U>{
fn print_all_name(&self){
println!("master name: {}",self.master.get_name());
println!("student name: {}",self.student.get_name())
}
fn print_all_age(&self){
println!("master age: {}",self.master.get_age());
println!("student age: {}",self.student.get_age())
}
};
let t = Teacher { name: "李老师".to_string(), age: 23, subkey: vec!["政治".to_string()] };
let s = Student { name: "小环".to_string(), age: 12 };
let p1 = People{
master:t,
student:s
};
p1.print_all_age();
p1.print_all_name();
// 10. 使用 trait 对 trait 进行约束
trait GetN{
fn get_name(&self) -> &String;
}
trait PrintN{
fn print_name(&self);
}
// 为所有拥有GetN行为的类型 实现PrintN
impl <T:GetN> PrintN for T{ // 这代码的意思就是 impl 特征 for 结构体, 只要实现了 GetN 特征的结构体, 也同样实现了 PrintN的特征, 其中 T 这里是泛型且是结构体, 且由于 我们在下面代码实现了 PrintN特征中的print_name函数的默认实现
fn print_name(&self){
println!("我叫: {}", &self.get_name())
}
}
// 定义结构体
struct Stu1{
name:String
}
// 使用特征 对结构体方法进行约束
impl GetN for Stu1{
fn get_name(&self) -> &String{
&self.name
}
}
let s1 = Stu1{name:"小山".to_string()};
s1.print_name() // 由于有了 GetN特征, 这里也就有了 PrintN 的特征, 且PrintN中的print_name方法我们有默认实现
}
fn stu17() {
println!("stu17: 生命周期");
// 生命周期参数是为了帮助借用检查器验证非法借用, 防止垂悬指针; 函数间传入和返回的借用必须相关联, 当然单独返回标注 'static 的字面量之后就为 一个全局的变量,可以认为是处于栈的底部
// 生命周期说白了就是作用域的名字, 每一个引用以及包含引用的数据结构, 都要有一个生命周期来指定它保持有效的作用域
// 1. 函数中的声明周期(错误示范)
// fn gt_lt(x:& str, y:& str) -> &str { // error: expected named lifetime parameter
// if x.len() > y.len() {
// x
// }else {
// y
// }
// };
// 1.1 函数中的参数的显式生命周期, fn function<'a >(x:&'a str, y:& str), 在函数名后<> 声明, 保证返回的值不会变成悬垂引用, 让编译器知道 传出参数的生命周期同传入的参数的生命周期,比函数体内的长不会造成悬垂引用
fn gt_lt<'a >(x:&'a str, y:& str) -> &'a str { // 这里参数 y 并不需要声明生命周期
if x.len() > y.len() {
x
}else {
x
}
};
let str = gt_lt("hello", "hh");
println!("比较大小后: {}", &str);
// 1.3 声明周期始终和引用借用相关, 返回值的生命周期参数需要与一个参数的生命周期参数相匹配,如果没有匹配上那么就会造成垂悬引用,解决方案是返回一个有所有权的数据类型而不是一个引用
// fn get_str<'a>(a:&str)->&'a str{ // error: returns a value referencing data owned by the current function 垂悬引用,因为传进来的参数和返回值没有关系,
// let ret = String::from("really long string");
// ret.as_str()
// };
fn get_i32()->i32 { 2 } // 一个返回所有权的函数
// 2. 结构体中的生命周期,如果用到了引用借用类型的参数, 则需要声明生命周期
// 结构体实例的生命周期应短于或等于任意一个成员的生命周期
// struct A{ name:&str } // error: expected named lifetime parameter
struct A<'a>{ name:&'a str }; // 这个结构体存在一个字段 是引用的类型 所以必须在结构体名称后面使用尖括号 <> 来进行声明
let zs = "zs".to_string();
let a = A{ name: &zs};
// {zs;} // 禁止drop 因为后续还在使用 结构体的实例
println!("结构体如果使用了引用类型的参数需要 'a 来声明生命周期 a.neme:&str = {}", a.name);
// 3. 生命周期的省略, 可以用于 impl 和 fn 代码块,如下规则可以不用显式的声明生命周期
// 3.1 每个引用的参数都要有他的生命周期,所以每个省略的生命周期参数 都会隐性添加生命周期
fn get_3_1<'x, 'y>(a:&'x String, b:&'y String)->&'x String{a}
fn get_3_1_<'x>(a:&'x String, b:& String)->&'x String{a} // 这里因为没有返回 b 相关的,所以可以不用声明生命周期
// 3.2 如果只有一个输入生命周期参数, 那么它被赋予所有的输出生命周期
fn get_3_2(a:&str) -> &str{ a } // 只有一个参数 编译器会推导函数为: fn get_str<'a>(a:&'a str)->&'a str{ a }
// 3.3 如果在方法中有多个输入生命周期参数, 如果 该方法中有 &self/self 那么 &self/self 的生命周期被赋予到该方法的所有输出的生命周期 如下
// 4. 方法中的生命周期
struct Student<'a>{
name:&'a String,
}
impl<'c> Student<'c>{ // 这里'c 只是对应属于结构体中 name 的生命周期
// fn get_str(& self, s:& str) -> & str { // 这里满足生命周期省略的第三条规定 返回的 &str 的声明周期对应的是 self 的生命周期
fn get_str<'b>(&'b self, s:& str) -> &'b str { // 上面等价于这一行
"get_str"
}
// fn get_str_s(& self, s:& str) -> & str { // 这里返回了s, 这里输出的生命周期参数是同 self的,如上,返回的 &str 相当于 &'b str, 其中 'b 是属于self的生命周期, 而返回的 s 和self是不同的生命周期, error: this parameter and the return type are declared with different lifetimes
// s
// }
fn get_str_(s:&str) -> &str{
"get_str_"
}
}
let s1 = Student { name: & "zs".to_string() };
println!("get_str: {}", s1.get_str("hh"));
println!("get_str_: {}", Student::get_str_("hh"));
// 5. 静态生命周期: 使用 'static 定义, 存活在整个程序期间, 所有的字符字面值默认都有 'static 生命周期: let s:& 'static str = "hh"
let c:& 'static str = "2233"; // 默认的生命周期
let c:& str = "2233"; // 默认添加了 'static 生命周期
// 6. 结合泛型类型参数、trait bounds 和生命周期
use std::fmt::Display;
fn func<'x, T:Display>(a:&'x str, b:&'x str, c:T) -> &'x str{ // 这里是引用类型, 手动指定的生命周期
println!("c: {}", c);
if a.len() < b.len(){
a
}else {
b
}
}
let c = "2233";
let r = func("hh", "hei", c);
println!("r: {}", r);
//7. 标注多个生命周期,
// fn gt_lt_1<'a, 'b>(a:&'a str, b:&'b str) -> &'a str{ // 因为编译器无法判断这两个生命周期参数的大小,此时可以显式地指定'a和'b的关系
// if a.len() > b.len(){a}else { b }
// }
fn gt_lt_1<'a:'b, 'b>(a:&'a String, b:&'b String) -> &'b str{ // 'a:'b 的意思就是 'b 是 'a的子集, 'a的存活时间长于'b, 所以返回的生命周期参数是 'b的同事 也满足了 'a, 因为'a比'b长
if a.len() > b.len(){a}else { b }
}
let s1 = "hi".to_string();
let s2 = "hello".to_string();
let s3 = gt_lt_1(&s1, &s2);
println!("经过比较 s3: {}", s3);
{s1;} // 这里提前drop掉 s1 也不会影响, 因为生命周期声明是帮助编译器检查参数的一切关系的
// 8. 结构体实例的生命周期应短于或等于任意一个成员的生命周期 否则编译无法通过
#[derive(Debug)]
struct Stu<'a>{name:&'a String}
let mut s = "小明".to_string();
let mut s1 = Stu { name: &s };
// {
// s; // 这里让 s 提前drop, 会导致编译不通过 Error: move out of `s` occurs here
// }
// s = "小王".to_string(); // Error: assignment to borrowed `s` occurs here 这里s在上方已经被借用了, 所以无法修改了
println!("s1: {:?}",s1);
// 9. trait对象的生命周期 (1. trait对象的生命周期默认是'static; 2. 如果实现trait的类型包含&'a X 或&'a mut X,则默认生命周期改为'a; 3. 如果实现trait的类型包含多个类似T:'a的从句,则生命周期需要明确指定)
struct P<'a>{p:&'a i32} // 定义结构体
trait P_trait{};
impl<'a> P_trait for P<'a>{};
let p = P { p: &123 };
let p_dyn = &p as & dyn P_trait;
// 9.1 需要需要明确指定trait对象生命周期的例子
trait Ptrait_9_1<'x>{};
struct Pstruct_9_1<'a>{p:&'a i32};
impl<'a,'x> Ptrait_9_1<'x> for Pstruct_9_1<'a>{};
// 定义一个返回 trait对象的函数 这里需要明确指明返回的 trait对象的生命周期, 因为 dyn Ptrait_9_1<'a>是一个 trait对象, 需要添加 'a 来覆盖默认的 'static 生命周期
fn get_trait_object<'a>(p:&'a i32) -> Box<dyn Ptrait_9_1<'a> + 'a>{
Box::new(Pstruct_9_1{p}) // 外部接收一个参数, 在这里生成一个 Pstruct_9_1 的实例 并返回一个 trait 对象
}
let p_obj = get_trait_object(&18);
// 10. 方法中的self
let mut data = vec![1, 2,3];
let x = &data[0];
data.push(4);
// println!("{}", x); // 这里仍然使用了不可变借用 x, 那这里x的声明周期中间穿插了一个 push操作, push操作是使用了 &mut self, 违反了借用规则
}
fn stu18() {
println!("stu18: 闭包");
// 闭包是生成一个结构体,通过triat调用的, 闭包等价于一个实现了特定Trait的结构体
// 闭包作为返回值返回必须使用 trait 对象才能返回
// 1. 定义方式( 闭包可以推导返回类型 )
// 闭包通过 move 关键字可以对环境中的 自由变量 的所有权 手动转移到闭包当中
// 闭包会创建新的作用域, 对于环境变量默认使用不可变借用来捕获所有权,其次是可变借用,最后move
// 闭包获取变量的三种方式 (1. 获取所有权FnOnce:获取所有权; 2. 可变借用FnMut:获取可变的借用值所以可以改变环境; 3. 不可变借用Fn)
// 对于 copy 类型的变量, 如果使用 move 则会使用使用变量的Copy的新变量, 而 对于移动语义的变量则会强制执行获取所有权
let func1 = | x:u32 | -> u32 { x + 1};
let func2 = | x:u32 | -> u32 { x };
let func3 = | x:u32 | x; // 推导出了返回类型
// 2. 闭包可以获取到上下文的变量其内部是一个封闭环境
let mut flag: i32 = 1;
let add_1 = |x: i32| x + flag;
// flag = 3; // error: assignment to borrowed `flag` occurs here
let flag = 2; // 封闭环境再次修改这个变量, 影响不到闭包函数中
let n: i32 = 3;
let n_1 = add_1(n);
println!("n+i 之后: {}", n_1);
// 3. 使用带有泛型 和 fn trait 类型的闭包
// 创建一个结构体其中需要有回调函数和值, 回调函数通过 trait bound语法 添加特征约束 为 Fn(u32) -> u32
struct Cacher<T>
where T: Fn(u32) -> u32
{
callback:T,
value:Option<u32>
};
impl<T> Cacher<T>
where T:Fn(u32) -> u32
{
fn new(callback:T) -> Cacher<T> { // 关联函数, 创建一个实例, 用来实现缓存功能, 新的实例 value为None
Cacher {
callback,
value:None
}
}
fn value(&mut self, args:u32) -> u32 { // 实例方法, 如果已经有了value 则返回现有value, 如果第一次调用没有value走向None分支 使外部传进来的args参数调用回调函数返回外部
match self.value {
Some(V) => V,
None => {
let v = (self.callback)(args);
self.value = Some(v);
v
}
}
}
}
// 3. 获取环境中的变量有三种方式
// 创建一个闭包时 Rust自动推导如何使用外部变量,所有闭包都可以被调用至少一次, 因此所有闭包都实现了 FnOnce,
// == 号的实现 使用的是 &self 捕获的是不可变借用, 所以这里没有发生所有权转移
let z = String::from("hello");
let fn1 = |f: String| {f == z};
fn1("123".to_string());
println!("{}",z); // 所有权没有转移
// == 号的实现 由于使用的是 &self, 捕获的是不可变借用, 所以不会捕获外部的所有权
let x = vec![1,2,3];
let fn2 = | f | f == x;
fn2(vec![1,2,3]);
println!("{:?}",x); // 所有权没有转移
// 3.1 使用 move 强制手动转移所有权
let x = vec![9,8,7];
let fn2 = move | f | f == x;
fn2(vec![9,8,7]);
// println!("{:?}",x) // 这里由于闭包使用了 move 所以所有权已经转移了 error: value borrowed here after move
// 4. 对于移动语义的变量在闭包捕获时会发生所有权转移
let s = String::from("hello");
let f1 = || { s; }; // 后续无法再使用变量 s
f1(); // Fnonce
// println!("{}",s); // Error: value borrowed here after move
// f1(); // Error: value used here after move
// 5. Fnmut例子 因为闭包内的数据发生了变化 所以需要 fn指针 本身是可变的
let mut ss = "hello".to_string();
let mut f2 = || { ss+="123"; };
f2();
f2();
// 6. 闭包的逃逸性
// 逃逸闭包必须复制或移动环境变量. 这是很显然的, 如果闭包在词法作用域之外使用, 而其如果以引用的方式获取环境变量, 有可能引起悬垂指针问题
// closure 在捕获外部变量的时候,如果外部变量是引用类型,或者外部变量内部包含引用类型, 或者闭包内捕获的是外部的不可变引用, 如果闭包在词法作用域之外使用(比如多线程中), 会造成垂悬指针的问题
// 对某一类型的'static约束不是用来控制这个类型的对象可以存活多久;'static约束控制的是这个类型的对象所包含的引用类型所允许的生命周期
// 这种情况下 需要使用 move
fn test()->Box<dyn Fn() -> ()>{
// let x = 123;
// let c: Box<dyn Fn()> = Box::new(|| {
// let d = x;
// x; }); // 这里 闭包捕获 的是外部变量 因为i32 实现了 Copy trait 所以这里
// let x = &123;
// let c: Box<dyn Fn()> = Box::new(|| {
// let d = x;
// x; }); // 这里 闭包使用 &i32 不可变借用
// let x = "123";
// let c: Box<dyn Fn()> = Box::new(|| {
// let d = x;
// x; }); // 这里 闭包使用 &str 不可变借用
// let s = "123".to_string();
// let x = &s;
// let c: Box<dyn Fn()> = Box::new(move || {
// let d = x;
// x;}); // 这里 闭包使用 &String 不可变借用
// println!("s 依然可以使用: :?{}", s);
// let x = "123".to_string(); // 这个例子不在讨论之中,只是乱入的,只是解释一下:这里变量x 在词法作用域结束之后已经被x 被 drop 了,因为 x 没有实现Copy
// let c: Box<dyn Fn()> = Box::new(move || { x; });
// 使用 move 强制执行 Copy
let x = "123";
let c: Box<dyn Fn()> = Box::new(move || { x; });
return c;
// 这之后, 此词法作用域之内的变量被 drop
};
let f1 = test();
f1();
// 7. 闭包作为返回值返回必须使用 trait 对象才能返回
// 7.1 这里使用impl 可以对返回的变量进行约束, 但 impl返回的只是类型占位 , 是非常局限的只支持单分支的代码,如果出现分支 由于impl 静态的,出现多分支的时候会出现闭包类型不一样
// fn get_fn1() -> impl Fn(i32,i32)->i32{
// let x = |x, y| x + y;
// x
// }
// let function = get_fn1();
// 7.2 多分支使用 impl 返回 闭包 Error: expected closure, found a different closure
// fn get_fn3(flag:i32) -> impl Fn() -> (){
// if true{
// return move ||{flag;}
// }
// else {
// return move ||{flag;}
// }
// }
// 7.2 闭包作为返回 trait对象
fn get_fn2()-> Box<dyn Fn(i32,i32)->i32>{
let x = |x, y| x + y;
Box::new(x)
}
let function = get_fn2();
}
fn stu19() {
println!("stu19: 迭代器");
// 1. 迭代器负责遍历序列中的每一项和决定序列何时结束的逻辑
// 2. 迭代器创建之后是惰性的, 在调用之前不会有任何效果
// 3. 每个迭代器都实现了 iterator trait, 定义在标准库中, (next 是被要求实现的唯一的一个方法, next一次返回一个元素,当迭代器结束的时候 返回None)
// trait Iterator{
// type Item;
// fn next(&mut self) -> Option<Self::Item>; // type Item 和 Self::Item 这种用法叫做定义 trait 的关联类型
// }
// 迭代器常见的三种: 1. IntoIter,转移所有权,对应self; 2. Iter,获得不可变借用,对应&self; 3. IterMut,获得可变借用,对应&mut self
// for循环实际上是一个语法糖, for循环真正循环的,并不是一个迭代器(Iterator),真正在这个语法糖里起作用的,是 IntoIterator, 内调用了 into_iter 使用了 self 的方法
// 1. 迭代器使用
let a = vec![1, 2, 3, 4];
// // for i in a{ // 如果不用迭代器遍历数据, 则会发生所有权转移 下面的循环就无法运行了
// // println!("不用迭代器: {}",i)
// // }
let a_iter1 = a.iter(); // 这一步不会对 a 造成任何影响
for i in a_iter1{
println!("使用迭代器: {}",i)
}
// for i in a.iter(){
// println!("使用迭代器: {}",i)
// }
// 2. next 方法 迭代不可变引用
let a = vec![1, 2, 3];
let mut a_iter = a.iter();
if let Some(v) = a_iter.next() {
println!("next1: {}", v) // 1
}
if let Some(v) = a_iter.next() {
println!("next1: {}", v) // 2
}
if let Some(v) = a_iter.next() {
println!("next1: {}", v) // 3
}
if let Some(v) = a_iter.next() {
println!("next1: {}", v) // None
}else {
println!("已经空了: None")
}
// next 方法 迭代可变引用
let mut a = vec![1, 2, 3];
let mut a_iter = a.iter_mut();
if let Some(v) = a_iter.next(){
*v *= 10
}
println!("改变了: {:?}", a);
// 自定义一个迭代器
struct Counter{ // 定义结构体
count:u32
};
impl Counter{ // 定义 new 函数
fn new()->Counter{
Counter{
count:0
}
}
};
impl Iterator for Counter{ // 定义 Iterator 特征
type Item = u32;
fn next(&mut self)-> Option<Self::Item>{
if self.count < 5 {
self.count += 1;
Some(self.count)
}else {
None
}
}
}
let mut counter = Counter::new(); // 生成实例
for i in 0..10 {
if let Some(v) = counter.next(){
println!("实现的next方法, 实例调用 next 方法: {}",i)
}else {
println!("实现的next方法, 调用完毕");
break
}
}
// 查看迭代器边界信息(使用 size_hint 方法获取一个迭代器状态, 返回一个元组内有两个元素: 一个元素表示下限(lower bound),第二个元素表示上限(upper bound))
let mut lis = vec![1, 2, 3, 4, 5];
let mut lis_iter = lis.iter();
lis_iter.next();
lis_iter.next();
lis_iter.next();
println!("lis_iter.size_hint(): {:?}", lis_iter.size_hint()); // 此时经过了三次迭代打印信息
// 使用迭代器追加字符串
let mut s = "hello".to_string();
s.extend(vec!["1", "2"]);
println!("字符串增加后: {}", s);
// 迭代器返回指定下标元素 使用 nth方法 获得指定下标的元素(注意此方法会消费迭代器指定下标元素之前的迭代器)
let v = vec![2, 3, 1, 45];
let mut v_iter = v.into_iter();
let idx_2 = v_iter.nth(2);
println!("idx_2: {:?}",idx_2);
// slice 数组使用迭代器(在for循环中使用 &arr 可以自动转换为迭代器)
let mut lis = [1, 2, 3, 4];
for i in &lis{}; // 引用自动实现了迭代器
for i in lis.iter(){};
for i in lis.iter_mut(){};
// 适配器
let v1 = vec![1,2,3];
let mut v1_iter = v1.iter();
let sum:u32 = v1_iter.sum(); // 调用消费适配器
println!("调用消费适配器之后求和为: {}",sum);
// if let Some(v) = v1_iter.next() { // 这里直接所有权被移走了
// println!("{}", v)
// } else { println!("调用消费适配器之后,迭代器内已经没有值了 {:?}", v1_iter) }; // 不会走到这一步因为所有权都已经被移走了
// 迭代适配器 map 映射
let v1 = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
println!("迭代适配器 映射 之后的值: {:?}", v2);
// 迭代过滤器 filter 过滤
let v1 = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().filter(|x| **x< 3 ).collect();
println!("迭代适配器 过滤 之后的值: {:?}", v2);
// 迭代器翻转 rev (使用 rev 得到一个翻转的迭代器, 其本质是调用本身迭代器的 next_back 方法)
let lis = vec![1, 2, 3];
// lis.iter().next_back();
let lis_back = lis.iter().rev(); // 得到一个翻转的迭代器
// 消费器 any
let v = vec![1, 2, 3, 4, 5];
let mut v_iter = v.iter();
println!("查找容器中是否存在满足条件的元素: {}", v_iter.any(|&x| x == 2));
// 消费器 fold
let v = vec![1, 2, 3, 4, 5];
let mut v_iter = v.iter();
println!("第一个为初始值,第二个为带有两个参数的闭包。其中闭包的第一个参数被称为累加器: {}",v_iter.fold(100, |acc, x| acc + x));
// ## 消费器 collect(使用时需要显示添加收集后类型); collect消费器有“收集”功能,在语义上可以理解为将迭代器中的元素收集到指定的集合容器中
let v = vec![1, 2, 3, 4, 5];
let mut v_iter = v.iter();
let mut new_iter = v_iter.map(|x| x * x); // 加法操作会自动解引用
// let new_vec:Vec<i32> = new_iter.collect(); // 定义变量时 使用类型注解
let new_vec = new_iter.collect::<Vec<i32>>(); // 显式的添加类型
println!("显式的添加类型, 使迭代器转为一个Vec: {:?}",new_vec);
// ##自定义集合实现FromIterator
use std::iter::FromIterator;
#[derive(Debug)]
struct MyVec(Vec<i32>); // 创建结构体
// 定义结构体方法
impl MyVec{
fn new()->MyVec{
MyVec(Vec::new())
}
fn push(&mut self, value:i32) -> (){
self.0.push(value)
}
}
// 实现FromIterator
impl FromIterator<i32> for MyVec{
fn from_iter<I>(iter:I) -> Self // 接收一个泛型
where I:IntoIterator<Item = i32> // 其泛型必须满足具体的特征约束
{
let mut c = MyVec::new();
for i in iter{
c.push(i)
};
c
}
}
// 使用 MyVec::from_iter
let iter = vec![1, 2, 3, 4].into_iter();
let mut my_vec = MyVec::from_iter(iter);
println!("my_vec: {:?}", my_vec);
// 使用 collect
let iter = vec![2, 2, 3, 4].into_iter();
let mut my_vec:MyVec = iter.collect();
println!("my_vec: {:?}", my_vec);
// 使用 collect
let iter = vec![3, 2, 3, 4].into_iter();
let mut my_vec:MyVec = iter.collect::<MyVec>();
println!("my_vec: {:?}", my_vec);
}
fn stu20() {
println!("stu20: 智能指针");
// 指针是一个包含内存地址的变量, 数据保存在堆上, 而栈上保存的是 数据 在堆上的地址, 除了数据被存储在堆上以外,Box没有任何性能损失
// 对于Box<T>类型来说,如果包含的类型T属于复制语义,则执行按位复制;如果属于移动语义,则移动所有权
// 智能指针拥有继承自 Box<T> T 变量中的方法
// 智能指针是一种数据结构, 类似于指针,但是却有额外的信息,比如有一些引用计数, 当智能指针没有任何所有者的时候清除数据
// 普通引用和智能指针的区别: 引用只是借用数据的指针, 而智能指针则是拥有他们指向的数据
// 智能指针一般使用结构体实现, 其实现了 Deref(解引用操作的相关操作) 和 Drop(允许自定义只能指针离开作用与执行的代码) 这两个 trait
// drop 标志储存在栈中, 并不在实现 Drop 的类型里。
// 适用场景:当有一个未知大小类型比如String,但是上下文中使用这个变量又需要知道大小, 当有大量数据希望不copy的情况下转移所有权, 当希望有一个值只关心他的类型是否实现了特定trait并不关心具体类型的时候
// Box<T>使用操作符(*)进行解引用,对于未实现Copy trait 的类型, 可能会出现转移所有权的行为, Rc<T>和Arc<T>不支持解引用移动
// 1. String类型实现 Deref 返回的是 &str, 如果一个类型 T实现了Deref<Target=U>,则该类型T的引用(或智能指针)在引用的时候会被自动转换为类型U
// String类型实现的add方法的右值参数必须是&str类型, &s2这里自动是&String类型,会被自动隐式转换为&str
let s1 = "hello".to_string();
let s2 = "world".to_string();
let s3 = s1 + &s2; // 这里 &String 会被转换为 &str
// let s3 = s1 + &&&*&&*&&&&s2.deref();
let s4 = s3.deref();
// 1. 基本使用
let a = Box::from(1);
println!("BOX a.to_string(): {}", a.to_string());
// enum List { // 定义一个链表 递归类型 这里在使用的会后会出现问题,因为无法知道 list占用的大小
// Cons(i32,List),
// Nil
// }
enum List { // 定义一个链表 递归类型 使用 Box<T> 可以进行递归 得到大小,因为 Box<T>存储的是指针,指针又在栈上,任何 List 值最多需要一个i32 加上 box 指针数据的大小
Cons(i32,Box<List>),
Nil
}
use List::Cons;
use List::Nil;
// 创建链表, 保存的是栈上的数据(堆上的地址), 所以一开始就是知道栈上大小的,
let list = Cons(2,
Box::new(Cons(4,
Box::new(Cons(5,
Box::new(Nil))))));
// Deref 实现 Deref trait 即可使用解引用操作
let a = 5;
let b = &a;
assert_eq!(*b, a); // 解引用 由此可得知 b 存的是 a 的地址
let box_a = Box::from(a); //
assert_eq!(*box_a, a); // 解引用 智能指针, 由此可见 智能指针指向了 a 的地址
// 2. 手动实现一个 Box
use std::ops::Deref;
struct MyBox<T>(T); // 创建结构体
impl<T> MyBox<T>
{
fn new(x:T)->MyBox<T>{
MyBox(x)
}
}
// 3. 实现解引用 "*" 操作 需要实现实现标准库的 Deref 特征 名为 deref 的方法返回 值的引用, 在我们使用 *a 的时候, 内部做的就是 *(a.deref()), 通过调用deref得到引用 再解引用
// Rust内将 * 运算符替换为 deref 方法调用和一个普通解引用,我们就不需要调用 deref 方法了
// impl<T> MyBox<T> // 特征约束 这里我傻逼了记混了特征约束/特征实现
// where T:Deref // 特征约束
impl <T> Deref for MyBox<T> // 特征实现
{
type Target = T;
fn deref(&self) -> &T { // 这里使用 &self 不希望解引用的时候拿掉 实例的所有权
match &self.0 {
i32=>{
println!("发现了一个 i32 类型")
}
}
&self.0 // 这里返回引用,是我们不希望内部值得所有权被移出
}
}
let x = 2;
let a = MyBox::new(x);
assert_eq!(*a,x);
// 3.1 自动解引用(变量 a 是Box<&str>类型,它并没有实现过chars()方法;但是现在可以直接调用,因为Box<T>实现了Deref<Target<T>>, 使用起来完全透明,就好像Box并不存在一样)
let a = Box::new("hello");
println!("{:?}", a.chars());
// 4. 解引用多态与可变性交互
// 如果一个类型 T实现了Deref<Target=U>,则该类型T的引用(或智能指针)在应用的时候会被自动转换为类型U
// 4.1 当T: Deref<target=U>时, 从 &T 变成 &U类型的引用, 一个 &T 而 T 实现了 U 类型的 Deref ,则可以直接得到 &U, 比如 String类型的deref得到的就是&str
// 4.2 当T: DerefMut<target=U> 时, 从 &mut T 变成 & mut 的引用
// 4.3 当T: Deref<target=U> 时, 从 &mut T 变为 &U, 不可能从 & T 变成 & mut T因为同一个变量只能有一个可变引用
let s = MyBox(String::from("hehe"));
let function_1 = |x: &str| println!("function_1, 解引用多态后: {}", x); // 这里将MyBox 变为MyBox<String>,再将 String的解引用 变成字符串的slice &str, 目标类型: &str, 初始类型: &String 都是不可变的 符合 4.1
function_1(&(*s)[..]); // 如果没有实现解引用多态时需要这样调用 (*s) 将 MyBox<String> 解引用为 String, [..]得到slice 再使用 & 得到引用
function_1(&s);
// 5. Drop trait: 当值离开作用域的时候,执行次函数的代码, drop函数需要传入可变引用, 因为要修改/清空变量内的数据
impl<T> Drop for MyBox<T>{
fn drop(&mut self){
// self.count -= 1 上面传入 & mut self 原因就是因为可能会计算并修改实例的引用计数,
println!("清理了一个变量")
}
}
{
println!("----开始声明a变量");
let a = MyBox(123);
}
println!("----这里a变量已经被清理");
// 6. Drop 提前释放, rust 提供了一个 std::mem::drop() 通过传递希望提早强制丢弃的值作为参数
struct Student{
name:String
}
impl Drop for Student{
fn drop(&mut self){
println!("被释放了: {}",&self.name)
}
}
let s = Student { name: "小明".to_string() };
drop(s); // 提前释放
// println!("生成了一个: {}", s.name); // error: value borrowed here after move
// 7. 使用操作符(*)进行解引用可能会出现转移所有权的行为
let bs = Box::new(String::from("hello"));
let bi = Box::new(123);
*bs; // 这一解引用之后所有权已经被转移走 let _ = *bs
*bi; // bi则不会因为 bi 实现了Copy trait
// println!("bs:{}",bs); // Error: value borrowed here after partial move
println!("bi:{}",bi)
}
fn stu21() {
println!("stu21: Rc智能指针");
// 通过RC<T> 允许通过不可变引用来 只读的 在 程序的 多个部分, 共享数据不允许改变, 共享数据 (如果是多个可变引用,会违反借用规则之一,可能会造成数据竞争)
// 它不影响包裹对象的方法调用形式(即不存在先 解开 包裹再调用值这一说)
// 1. 如果有多个变量 想同时拥有某保存在堆上数据的所有权, 则需要使用智能指针, 否则 1个堆上数据只能有一个变量拥有其所有权
enum List{
Cons(i32,Box<List>),
Nil
};
use List::{Cons, Nil};
let li_1 = Cons(2,
Box::new(Cons(4,
Box::new(Nil))));
let li_3 = Cons(12, Box::new(li_1));
// let li_4 = Cons(29, Box::new(li_1)); // error: value used here after move
// 2. 使用 Rc 智能指针重新定义 链表, 并使用 Rc::clone() 或 &self.clone(), 来不转移所有权的情况下 共享数据, 推荐使用 Rc::clone Rc::clone 的实现并不像大部分类型的 clone 实现那样对所有数据进行深拷贝; Rc::clone 只会增加引用计数,这并不会花费多少时间
enum Li {
Item(i32, Rc<Li>),
Null
}
use Li::{Item,Null};
use std::rc::Rc;
let lis_1 = Rc::new(Item(8,
Rc::new(Item(17,
Rc::new(Null)))));
let lis_2 = Item(13, Rc::clone(&lis_1));
let lis_3 = Item(13, Rc::clone(&lis_1));
// 3. 引用计数: 通过 Rc::strong_count 来获取Rc的引用计数
{
let lis_3 = Item(13, lis_1.clone());
println!("las_1的引用计数为: {}", Rc::strong_count(&lis_1))
}
// 3. 获得 Rc 智能指针的引用计数
println!("las_1的引用计数为: {}", Rc::strong_count(&lis_1))
}
fn stu22() {
println!("stu22: RefCell");
// Cell<T>一般适合复制语义类型(实现了Copy),RefCell<T>一般适合移动语义类型(未实现Copy)
// Cell<T>无运行时开销,并且永远不会在运行时引发panic错误;RefCell<T>需要在运行时执行借用检查,所以有运行时开销,一旦发现违反借用规则的情况,则会引发线程panic而退出当前线程
// 内部可变性: 通过 borrow_mut 允许在使用不可变引用的时候获得变量的可变引用 并修改数据: 即使 Rc<RefCell<T>>
// RefCell<T> 在运行时检查借用规则,通常情况下检查作用规则是在编译阶段, RefCell<T>代表其数据的唯一所有权
// 类似于 RC<T> RefCell<T>只能用于单线程场景
// ---三种指针的区别
// Rc<T> 允许相同的数据有多个所有者; Box<T> 和 RefCell<T> 只允许有一个所有者
// Box<T> 允许在编译时执行不可变或可变借用检查; Rc<T>仅允许在编译时执行不可变借用检查, RefCell<T>允许在运行时执行不可变或可变检查
// 因为 RefCell 允许在运行时执行可变借用检查
// 1. 定义一个链表 其中 Item 第一个参数为 Rc<RefCell<i32>> 类型, 第二个为 Rc智能指针类型
// 使用 *Rc<RefCell<T>>.borrow_mut()可以得到 T 的可变借用,
#[derive(Debug)]
enum List{
Item(Rc<RefCell<i32>>,Rc<List>),
Null
}
use List::{Item,Null};
// 创建一个变量, 智能指针包裹的RefCell类型的变量,当做 Item 的第一个变量
let value = Rc::new(RefCell::new(5));
let a = Rc::new(Item(value.clone(),Rc::new(Null)));
let b = Item(Rc::new(RefCell::new(9)), Rc::clone(&a));
let c = Item(Rc::new(RefCell::new(1)), a.clone());
println!("a: {:?}",a);
println!("b: {:?}",b);
println!("c: {:?}",c);
// 2. 对于RefCell中的数据可以直接修改
let b = RefCell::new(123);
*b.borrow_mut() = 2233;
println!("Refcell 可以直接修改数据为: {:?}",b);
// 3. 修改 Rc<Refcell<T>> 中的 T
let b = Rc::new(RefCell::new(10));
*(*b).borrow_mut() *= 20;
println!("Rc<Refcell<T>> 修改内部数据: {:?}", b);
}
fn stu23() {
println!("stu23: 循环引用");
// 由于Rust 中变量只有在 strong_count 为0的时候才会被清理, 如果两个变量相互引用, 则出现递归,超过栈深度那么会导致栈溢出, 造成内存泄漏,
#[derive(Debug)]
enum List{
Item(i32, RefCell<Rc<List>>),
Null
}
// 定义一个获取最尾部元素的方法
impl List{
fn get_item(&self) -> Option<&RefCell<Rc<List>>> {
match self {
Item(_, item) => Some(item),
Null => None
}
}
}
use List::{Item, Null};
// 创建一个 RefCell<Rc<T>> 的List
let l1 = Rc::new(Item(1, RefCell::new(Rc::new(Null))));
println!("l1_strong_count: {}",Rc::strong_count(&l1));
println!("l1_get_item: {:?}", l1.get_item());
let l2 = Rc::new(Item(2,RefCell::new(Rc::clone(&l1)))); // 这里让 l2 直接使用 l1 的引用
println!("l2_strong_count: {}", Rc::strong_count(&l2));
println!("l2_get_item: {:?}", l2.get_item());
// 修改l1的尾部指向, 这里由于 get_item 返回的是Option<&RefCell<Rc<List>>> 所以可以直接使用 if let Some(v) 进行匹配
if let Some(last) = l1.get_item(){
*last.borrow_mut() = Rc::clone(&l2)
}
// 再次打印一下 l1 l2 的引用计数
println!("l1_strong_count: {}", Rc::strong_count(&l1));
println!("l2_strong_count: {}", Rc::strong_count(&l2));
// 由于修改了l1的尾部元素让其指向了l2 这里再次打印两者的尾部元素
println!("l1_get_item: {:?}", l1.get_item()); // # thread 'main' has overflowed its stack 栈溢出了
println!("l2_get_item: {:?}", l2.get_item());
}
fn stu24() {
println!("stu24: 弱引用");
// RefCell<Weak<List>> 通过Weak::new()来创建,然后通过Rc::downgrade(&self) 向内传递值
// 弱引用 通过 Rc::downgrade传递 Rc 实例的引用到Weak内, 调用Rc::downgrade会得到Weak<T>的智能指针,同时将 Weak count 加1
// Weak count 无需为 0, 实例在strong_count为0的时候drop,无视Weak count 的引用计数
// Weak<T> 通过 upgrade() 方法来获取弱引用中具体的内容
use std::rc::Weak;
#[derive(Debug)]
enum List{
Item(i32, RefCell<Weak<List>>),
Null,
}
impl List{
fn get_item(&self) -> Option<& RefCell<Weak<List>>>{
match self {
Item(_,item)=> Some(item),
Null => None
}
}
}
use List::{Item, Null};
let l1 = Rc::new(Item(1, RefCell::new(Weak::new())));
let l2 = Rc::new(Item(2, RefCell::new(Weak::new())));
println!("l1: {:?}, l2: {:?}", l1, l2);
println!("l1_strong_count: {}, l1_Weak_count: {}",Rc::strong_count(&l1), Rc::weak_count(&l1));
println!("l2_strong_count: {}, l2_Weak_count: {}",Rc::strong_count(&l2), Rc::weak_count(&l2));
// 获取 匹配得到 l2 的尾部, 并传递 l1的引用 到l1尾部
if let Some(item) = l2.get_item(){
let f = Rc::downgrade(&l1);
println!("f: {:?}",f); // 传入了 &l1 的引用, 得到Weak<T>的智能指针, 这里智能指针在下面作为值 传入到 *item.borrow_mut() 中
println!("f.upgrade(): {:?}",f.upgrade()); // 打印Weak<T>中的具体内容
println!("item: {:?}",item); // RefCell { value: (Weak) }
println!("item.borrow(): {:?}",item.borrow()); // 打印一下值: 这里 item.borrow_mut() 得到的是 &RefCell<Weak<List>>中的 Weak
println!("item.borrow_mut(): {:?}",item.borrow_mut()); // 打印一下值: 这里 item.borrow_mut() 得到的是 &RefCell<Weak<List>>中的 Weak
println!("*item.borrow_mut(): {:?}",*item.borrow_mut());
*item.borrow_mut() = f; // 这一步也就是将上面得到的 Weak<T> 智能指针 复制给 item 的 解 可变引用
};
println!("l1_strong_count: {}, l1_Weak_count: {}",Rc::strong_count(&l1), Rc::weak_count(&l1));
println!("l2_strong_count: {}, l2_Weak_count: {}",Rc::strong_count(&l2), Rc::weak_count(&l2));
// 获取 l1 的尾部, 并传递 l2 的引用 到尾部
if let Some(item) = l1.get_item(){
*item.borrow_mut() = Rc::downgrade(&l2)
};
println!("l1_strong_count: {}, l1_Weak_count: {}",Rc::strong_count(&l1), Rc::weak_count(&l1));
println!("l2_strong_count: {}, l2_Weak_count: {}",Rc::strong_count(&l2), Rc::weak_count(&l2));
println!("l1: {:?}, l2: {:?}", l1, l2);
// 树形结构1
#[derive(Debug)]
struct Person{
value:u32,
parent:RefCell<Weak<Person>>, // 父节点中 保存 weak弱引用, 如果是 rc 引用,那么则会出现无限递归
children:RefCell<Vec<Rc<Person>>>
}
let leaf = Rc::new(
Person {
value: 1,
parent:RefCell::new(Weak::new()),
children:RefCell::new(vec![])
}
);
let p = leaf.parent.borrow().upgrade();
println!("子节点 leaf: {:?}, 子节点的父节点: {:?}",leaf,p);
println!("子节点的引用, strong_count: {}, weak_count: {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
// 创建父节点
let branch = Rc::new(
Person{
value:2,
parent:RefCell::new(Weak::new()),
children:RefCell::new(vec![Rc::clone(&leaf)])
});
println!("父节点 branch: {:?}",branch);
println!("父节点的引用, strong_count: {}, weak_count: {}", Rc::strong_count(&branch), Rc::weak_count(&branch));
// 添加父节点信息到子节点上
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
let p = leaf.parent.borrow().upgrade();
println!("子节点 leaf: {:?}, 子节点的父节点: {:?}",leaf,p);
println!("子节点的引用, strong_count: {}, weak_count: {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
println!("父节点的引用, strong_count: {}, weak_count: {}", Rc::strong_count(&branch), Rc::weak_count(&branch));
// let a = RefCell::new(vec![Rc::new(123)]);
// let b = a.borrow();
// let c = vec![Rc::new(123)];
// println!("a: {:?}, b: {:?}, c: {:?}", a, b, c);
// let a = Rc::new(vec!['a', 'b']);
// let b:Option<&char> = a.get(0);
// let c:char = a[2];
}
fn stu25() {
println!("stu25: 多线程");
use std::thread;
use std::time::Duration;
/// 直接使用thread::spawn 生成的线程,默认没有名称,并且其栈大小默认为2MB
// 1. 创建一个线程程序 thread::spawn 会返回一个线程对象
let handle = thread::spawn(|| {
for i in 0..10{
println!("thread_i: {}",i);
thread::sleep(Duration::from_millis(1))
}
});
for i in 0..5 {
println!("main_i: {}", i);
thread::sleep(Duration::from_millis(1))
};
// 2. 阻塞线程: 使用 join 阻塞主线程, join会等待子线程执行完毕之后主线程才会继续往下运行 (等待子线程执行完毕)
handle.join();
// 3. move 移动所有权到线程中( 注意移动到线程中, 主线程中已经没有所有权了 )
let mut sum:Vec<u32> = vec![1, 2, 3];
let handle1 = move || {
println!("通过外部move到线程内部的变量: {:?}", sum)
};
let h1 = thread::spawn(handle1);
// println!("{:?}",sum); // error: borrow of moved value: `sum`
h1.join();
// 4. 消息传递 use std::sync::mpsc; let (tx, rx) = mpsc::channel(), 这里使用解构获取 tx: 发送端, rx: 接收端
// 异步无界Channel: 对应 channel 函数, 发送消息是异步的,并且不会阻塞。无界,是指在理论上缓冲区是无限的;
// 同步有界Channel: 对应 sync_channel 函数, 该Channel可以预分配具有固定大小的缓冲区,并且发送消息是同步的,当缓冲区满时会阻塞消息发送,直到有可用的缓冲空间。当该Channel缓冲区大小为0时,就会变成一个“点”,在这种情况下,Sender和Receiver之间的消息传递是原子操作
// 通道类似于 单所有权方式, 调用rx send 的时候 会发生 move 操作, 被发送的 变量 后续无法再次使用 (一旦将值发送到另一个线程后,那个线程可能会在我们 后续 再次使用 它之前就将其修改或者丢弃)
// 发送者或者接收者任一被丢弃时rust就认为通道被关闭了
// 发送者 tx 的 send 会返回 Result 类型<T, E>, 如果接收端已经被丢弃了, 将没有发送目标,此时发送会返回错误
// 接收者 rx 的 recv 方法 当发送通道关闭会返回一个错误
// 接收者 rx 的 recv 方法 是一个阻塞方法, 不用join即可阻塞主线程, rx的 try_recv 方法不会阻塞主线程运行
// 接收者 rx 本身实现了迭代方法, 迭代器内部实现了 rx的 recv 方法 源码中iterator中next调用self.rx.recv().ok(), 所以下面可以通过 for 循环阻塞 获取消息
// !!如果存在多个发送者(共享通道) 注意如果 tx 不被析构, rx在接收消息时会一直阻塞, 见下面 例子 7
use std::sync::mpsc;
// 4.1.1 mpsc::channel 创建多个生产者 单个消费者的通道
let (tx, rx) = mpsc::channel(); // 同步有界
// 创建一个线程
thread::spawn(move || {
let val = "hello".to_string();
let ret = tx.send(val).unwrap(); // 返回 Result 类型
// println!("{}", val) // 注意这里所有权已经被转移了
});
// 在主线程中接收
let msg = rx.try_recv(); // try_recv 不会阻塞主线程, 主线程运行到这里, 子线程还没运行所以这里接收的消息为 error
println!("在h1子线程中 try_recv 接收的是: {:?}",msg);
let msg = rx.recv(); // 这里接收者的 recv 方法是一个阻塞方法,所以不需要用join 阻塞, 所以会等待接收完毕才会向下执行代码
println!("在h1子线程中 recv 接收的是: {:?}",msg);
// 4.1.2 一次发送多个信息
let (tx, rx) = mpsc::channel();
thread::spawn( move ||{
let values = vec!["小明".to_string(), "小花".to_string(), "小亮".to_string()]; // 定义多个数据
for i in values{ // 循环发送数据
tx.send(i).unwrap();
thread::sleep(Duration::from_secs(1)) // 每次发送完毕, 睡眠 1 秒
}
});
for msg in rx {
println!("接收到线程内的消息: {}", msg)
}
// 4.1.3 多个生产者, 单个消费者, 多个生产者使用 mpsc::Sender::clone 传入消费者 tx 的引用 创建
let (tx, rx) = mpsc::channel();
let tx1 = mpsc::Sender::clone(&tx); // 创建另一个消费者
// 创建两个生产者
thread::spawn(move || {
let values = vec!["小明".to_string(), "小花".to_string(), "小亮".to_string()];
for i in values {
tx.send(format!("tx: {}",i).to_string()).unwrap();
thread::sleep(Duration::from_secs(1));
} // 闭包作用域结束, drop掉 tx, 关闭通道
});
thread::spawn(move || {
let values = vec!["张三".to_string(), "李四".to_string(), "王二".to_string()];
for i in values {
tx1.send(format!("tx1: {}",i).to_string()).unwrap();
thread::sleep(Duration::from_secs(1));
} // 闭包结束 drop 掉tx1, 关闭通道
});
for i in rx{
println!("取到的数据: {}", i)
}
// 4.2 创建单个生产者 多个消费者的通道
// use std::thread;
// use std::sync::mpsc;
// use std::time::Duration;
// 5. 互斥器 Mutex<T>
// 任意时刻, 只允许一个线程访问某些数据, 互斥器使用时, 需要先获取到锁, 使用后需要释放锁, 如果获取不到会阻塞线程, 等待别的线程释放锁
// Mutex 内部实现了 drop 方法, 锁在离开作用域的时候自动释放, Mutex实例通过 lock 获得锁
// Mutex 是一个智能指针, 在调用 lock 时, 返回一个 MutexGuard<i32> 的智能指针
// 锁可以提前释放
// 一旦获取了锁,就可以将返回值(在这里是 num )视为一个其内部数据的可变引用了, 所以可以 * 解引用
use std::sync::Mutex;
// 创建一个互斥器
let sum = Mutex::new(5);
{
let mut lock = sum.lock().unwrap();
*lock = 13; // 锁在下面离开作用域的时候自动释放 调用了 drop
drop(lock) // 提前释放锁
}
println!("Mutex<i32>: {:?}",sum);
// 5.1 Arc 智能指针,原子引用计数, 多线程多所有权, 多线程使用的 Arc 智能指针包裹互斥器(Rc不是线程安全的所以无法使用 如果多线程修改一个数据, 可以使用 Arc 智能指针 进行包裹)
// Arc 智能指针是基于线程安全的,
use std::sync::Arc;
let curr = Arc::new(Mutex::new(0)); // Arc 指针 创建一个包含 互斥锁的对象
// 创建10个线程
let mut lis = vec![];
for i in 0..10 {
let curr_clone = Arc::clone(&curr); // 为了防止所有权转移, 每次循环 需要 clone Arc指针, 等下传入到线程内部
let h = thread::spawn(move || { // 循环创建线程,并将 外部变量 curr_clone 的所有权 传入到内部闭包
{
let mut num = curr_clone.lock().unwrap(); // 得到锁, 如果没有得到锁, 那么等待
*num += 1;
println!("线程: {}, 对 sum 增加了1, 目前 sum 为 : {}", i, num); // 下一行作用域结束, 释放锁
}
thread::sleep(Duration::from_secs(1))
});
lis.push(h)
}
for handle in lis {
handle.join();
}
println!("线程调度完毕");
// 6. 读写锁 读线程只允许进行只读访问, 而写线程只能进行独占写操作
// 读锁和写锁要使用显式作用域块隔离开, 在离开作用域时自动释放, 否则会造成死锁
// 因为读锁和写锁不能同时存在
// 只要线程没有拿到写锁, RwLock<T>就允许任意数量的读线程获得读锁
use std::sync::RwLock;
let lock = RwLock::new(123);
{
let r1 = lock.read();
let r2 = lock.read();
}
{
let w1 = lock.write();
}
// 7. channel 死锁(共享通道 使用的还是MPSC(多生产者单消费者)队列), 下面的代码中, 在闭包结束的时候仅仅只drop了 tx_clone, 而 rx 会一直阻塞等待消息, 但是有没有了新的消息, 就导致了 tx 不会被drop, 所以造成了死锁
let (tx, rx) = std::sync::mpsc::channel();
for i in 0..5{
let tx_clone = tx.clone(); // 仅仅是在 for 循环中使用的 clone,而 clone 使用的是 &self, 所以不会造成 tx 在for循环之后被 drop
thread::spawn(move ||{
tx_clone.send(i);
}); // 这里闭包结束, 仅仅 drop 掉了 tx_clone
};
// drop(tx); // 显示 的 drop 掉 tx,在rx之上, 以关闭通道 可以避免死锁
for i in rx{
println!("例子7: channel 死锁, {}", i)
};
println!("如果发生死锁, 那么永远不可能执行到这里")
}
fn stu26() {
println!("stu26: 结构体");
// Rust 里, 结构体, trait, 枚举类型 impl块, 组成了对象
struct Dog{
name:String
};
impl Dog {
fn new(name:String) -> Dog{
Dog{
name
}
}
fn say(&self){
println!("{}: wangwang",self.name)
}
}
let d = Dog::new("小虎".to_string());
d.say();
// 2. 封装(主要是外部包, 外部包中如果没有使用 pub 声明的变量 和方法是无法在其他位置使用的)
// 3. Rust 没有继承, 但是Rust可以通过特征来进行行为的共享
trait A {
fn say(){
println!("默认的say")
}
}
struct Student;
impl A for Student{};
// 默认行为 也可以认为是继承了
Student::say()
}
fn stu27() {
println!("stu27: trait 对象");
// 什么是Trait Object呢?指向trait的指针就是Trait Object;假如Bird是一个trait的名称,那么&Bird和Box<Bird>都是一种Trait Object 一个胖指针;
// 类似鸭子模型!! 注意 这里在使用 dyn修饰的变量会在编译器包装成为一个 trait Object
// trait本身也是一种类型,但它的类型大小在编译期是无法确定的,所以trait对象必须使用指针 &dyn trait, Box<dyn trait>; trait对象中包含两个指针:data指针和vtable指针
// 在rust 中, 泛型是惰性执行的, 对泛型使用 trait bound 编译器会单态化处理 进行 静态分发, 一旦确定类型, 则后续显然无法再改变, 比如 Clone, 所以这里需要使用 trait 对象
// 使用 trait 对象时, Rust 必须使用动态分发, 编译器无法知晓可能用于 trait 对象代码的类型,所以也无法知道该调用哪个类型的那个方法, 为此在运行时 使用 trait 对象的指针来知晓需要调用哪个方法, 而不是实例的方法
// !!! trait 对象要求对象安全,只有对象安全(object safe) 的 trait 才可以组成 trait对象, trait 对象必须要满足: 1. 返回值类型不为 Self:因为特征对象无法知道返回的self是什么类型(动态分配), 2. 方法中没有使用任何泛型类型参数 ,3. 第一个参数必须为 Self 类型或可以解引用为 Self 的类型, 4. self不能出现在除第一个参数之外的地方,包括返回值中
// !!! 总结一句话: 没有额外Self类型参数的非泛型成员方法
// 1. 首先看一个错误, 这里 Student 接收一个泛型,且需要实现 Stu trait, 首先 vec 内的第一个参数为 six, rust编译器推导 vec内部的所有元素都同 six 相同类型,但是后续出现了其他类型,这就导致了编译不通过,虽然他们都满足了泛型的条件
// 定义一个类型为Student, 内部有学生列表若干,其中分为six6年级的学生,seven7年级的学生, 他们有一个共同 特征就是 学习
trait Stu{ // 定义 trait
fn stu(&self);
}
struct Student<T> // 学生结构体, 需要同时包含 six 和 seven 年级, 这里使用泛型
where T:Stu
{
lis:Vec<Rc<T>>
}
impl<T> Student<T> // 学生调动其内的行为
where T:Stu
{
fn action(&self){
for i in self.lis.iter(){
i.stu()
}
}
}
struct Six{ // 定义年级结构体
name:String
}
struct Seven{ // 定义年级结构体
name:String
}
impl Stu for Six{ // 实现特征
fn stu(&self) {
println!("six {}, 正在学习", self.name)
}
}
impl Stu for Seven{ // 实现特征
fn stu(&self) {
println!("seven {}, 正在学习", self.name)
}
}
// 创建学生实例
let six = Rc::new(Six { name: "小亮".to_string() });
let seven = Rc::new(Seven { name: "小张".to_string()});
// 出错了: expected struct `stu27::Six`, found struct `stu27::Seven`
// let students = Student{
// lis:vec![six, seven] // 虽然他们都实现了 trait 中 的 stu 方法,但他们是不同类型, 是无法创建 vec
// };
// 2. 上方出现错误明显是编译器 认为 vec 内部的元素 T 都是统一类型的, 但是却出现了两种类型,虽然他们都是现实了 Stu trait
// 使用 dyn 修饰参数为 trait 对象, 替代上方的 泛型加约束的方式
// 重新定义
struct Student_ { // 重新定义 学生结构体, 使用 dyn 修饰, 表示这是一个 满足了 trait 对象 (一个胖指针)
lis:Vec<Rc<dyn Stu>>
}
impl Student_ {
fn st(&self){
for i in self.lis.iter(){
i.stu()
}
}
}
let students = Student_{
lis:vec![six, seven]
};
students.st();
// 3. 深入理解特征对象
use std::mem;
trait Bird {
fn fly(&self);
}
struct Duck;
struct Swan;
impl Bird for Duck {
fn fly(&self) { println!("duck duck"); }
}
impl Bird for Swan {
fn fly(&self) { println!("swan swan");}
}
fn print_trati_object(obj: &dyn Bird){ // trait object 类型,obj 是一个胖指针
// 使用不安全的 rust 获取胖指针内的相关信息
let (data, vtable):(usize, usize) = unsafe { mem::transmute(obj) };
println!("TraitObject [data:{}, vtable:{}]", data, vtable);
// 使用as执行强制类型转换,将vtable从 `usize` 类型转为 `*const usize` 类型
unsafe {
let v: * const usize = vtable as * const () as * const usize;
// 打印出指针 v 指向的内存区间的值
println!("data in vtable [{}, {}, {}, {}]",
*v, *v.offset(1), *v.offset(2), *v.offset(3));
}
}
print_trati_object(&Duck);
// 4. trait Object在内存中的表现(可以通过 as 对实例强制类型转换为 trait 对象)
// 获得一个实例
let duck = Duck;
// 获得一个实例的引用
let duck_borrow = &duck;
// 手动获得一个trait对象
let duck__ = duck_borrow as &dyn Bird;
// 分别打印大小(针对对象取指针,得到的是普通指针,它占据 64bit 的空间;如果我们把这个指针使用as运算符转换为Trait Object,它就成了胖指针,携带了额外的信息)
println!("Size of duck_ {}, Size of duck__ {}", mem::size_of_val(&duck_borrow), mem::size_of_val(&duck__));
// 5. 对象安全
// 通过 Self加上Sized 限制胖指针中的函数运行, 虽然是对象安全的, 但是 trait对象 无法调用
// Self表示 "调用者" 的类型, 这里 eat 调用者是 trait对象, trait对象编译时无法确定大小
trait Action{
fn say(&self){println!("say! say! say!");}
fn eat(&self) where Self:Sized {println!("eat! eat! eat!");} // 这里 给eat 添加了 Self:Sized 约束, 那么在trait对象中将无法使用这个方法, trait对象无法确定大小
}
struct People;
impl Action for People{};
fn action(s:& dyn Action){
// s.eat(); // Error: this has a `Sized` requirement; 无法编译通过, 因为eat 添加了Self:Sized限定 这就必须保证 trait对象 大小是可知的,然而无法可知
s.say();
};
action(&People);
// 尝试获取一个 trait对象 调用方法
let a = &People as & dyn Action;
a.say();
// a.eat() Error; this has a `Sized` requirement
// 6. 分离不安全的方法( 添加 Self: Sized 约束, 让 trait对象 不生成对应的方法)
trait Double {
// fn new() -> Self; // 使用这个会报错
fn new() -> Self where Self: Sized;
fn double(&mut self);
}
impl Double for i32 {
fn new() -> i32 { 0 }
fn double(&mut self) { *self *= 2; }
}
let mut i = 1;
let p : &mut Double = &mut i as &mut Double;
p.double();
// 7. 当函数中有Self类型作为参数或者返回类型时
// ## self Self的区别, self代表调用者, Self代表调用者的类型, 这里 Clone::clone 要求返回的需要和 调用者相同类型
// 因为 p 是一个 trait object,我们只知道,它是一个“胖指针”,包括了指向具体对象的指针以及指向虚函数表的指针;p指向的具体对象,它的类型是什么,我们已经不知道了,我们知道的仅仅是,这个类型实现了 Clone trait,其它的就一无所知了;而这个 clone() 方法又要求返回一个与 p 指向的具体类型一致的返回类型;对编译器来说,这是无法完成的任务
// pub trait Clone {
// fn clone(&self) -> Self;
// fn clone_from(&mut self, source: &Self) { ... }
// let p: &Clone = ...;
// let o = p.clone();
// 8. 当函数有泛型参数时
// x是trait object,通过它调用成员方法,是通过vtable虚函数表来进行查找并调用;现在需要被查找的函数成了泛型函数,而泛型函数在Rust中是编译阶段自动展开的, 在trait对象中没法把这些展开的函数都塞进虚表中,
// rust 禁止使用trait object来调用泛型函数,泛型函数是从虚函数表中剔除掉了的
trait SomeTrait {
fn generic_fn<A>(&self, value: A);
}
// fn func(x: &dyn SomeTrait) { // 无法编译, 因为有泛型则无法创建 trait对象 Error: this trait cannot be made into an object
// x.generic_fn("foo"); // A = &str
// x.generic_fn(1_u8); // A = u8
// }
// 9. trait对象 无法构造的一些条件:
// 9.1 当trait有Self:Sized约束时 (见上方, Self代表调用者的类型, trait对象中的 Self就是 trait对象类型, trait对象无法知道大小,只有运行时才知道)
// 9.2 当函数中有Self类型作为参数或者返回类型时 (见上方的clone)
// 9.3 当函数第一个参数不是self时 (如果一个 trait 中存在静态方法,而又希望通过 trait object 来调用其它的方法如果想调用参照上 静态方法后面加上 Self: Sized 约束,将它从虚函数表中剔除出去)
// 9.4 当函数有泛型参数时 见上方
}
fn stu28() {
println!("stu28: 模式");
// 模式分为可反驳 与 不可反驳的, 能匹配任何传递的可能值的模式是不可反驳的, 对值匹配可能会失败的模式是可被反驳的
// 不可反驳的模式有:match, 函数, let语句, for循环,因为通过不匹配的值,程序无法进行有意义的工作 必须匹配所有的条件
// 可反驳的模式: if let, while let 只能接受可反驳的模式,因为他们的定义就是为了有可能失败的条件 可以忽略一些条件
// let: let PATTERN = EXPRESSION
let x = 1;
let (a, b, c) = (1, 2, 3);
// 1.1 match 字面值匹配 必须匹配所有的情况 可以使用 | 或 操作符, 且必须返回同类型
let num = 1;
match num {
1 | 3 => println!("match 匹配到了: 1 | 3"),
2 => println!("match 匹配到了: 2"),
// i32 => ("类型匹配上了"),
_ => println!("match 通配符"),
}
// 1.2 匹配命名变量 (在 match 内是一个独立的作用域)
let x = Some(1);
let y = 2;
match x {
Some(3) => println!("x: 3"),
Some(y) => println!("y: {}", y), // 这里是独立的y, 是一个形参
_ => println!("other")
};
println!("x: {:?}, y: {:?}", x, y);
// 1.3 通过 .. 进行匹配
let x = 5;
match x {
0..=5 => println!("使用 0..=5"),
_ => ()
};
let c = 'c';
match c {
'a'..='z' => println!("使用 'a'..='z'"),
_ =>()
}
// if let, else if, else if let, else 根据条件匹配
let num = Some(2233);
let i = 13;
if let Some(2) = num{
println!("if let 匹配到了: {}", 2);
}else if let Some(111) = num{
println!("else if let 匹配到了: {}", 111)
}else {
println!("else 什么都没有匹配到")
}
// while let 只要模式匹配就会一直执行
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("while let : {}", top);
}
// for 匹配 (for 本身就是一个匹配模式)
let b = vec![(1,2),(3,4)];
for (c, ..) in b.iter(){
println!("c: {}", c )
}
// 解构结构体
struct Student{
name: String,
age:i32
}
let s1 = Student { name: "小亮".to_string(), age: 13 };
let Student{ name: n, age: a } = &s1;
let Student{ name, age } = &s1; // 同名的可以简写
println!("解构结构体: name: {}, age:{}",n,a);
println!("解构结构体: name: {}, age:{}",name,age);
assert_eq!(*a,13);
// match 匹配结构体 可以使用 .. 忽略
let n = String::from("小亮");
match s1 {
Student{name,age:13}=>println!("这个人匹配 年龄 为 13"),
Student{..}=>println!("这个人名字 年龄 为 小亮"),
_ => ()
}
// 解构枚举类型
enum Message{
Quit,
Move{x:i32,y:i32},
Write(String),
ChangeColor(i32, i32, i32)
}
let msg = Message::ChangeColor(255, 255, 255);
match msg {
Message::Quit => println!("匹配枚举类型的 Quit"),
Message::Move{x,y} => println!("匹配枚举类型的 Move: x: {}, y: {}", x, y),
Message::Write(text) => println!("匹配枚举类型的 Write: {}", text),
Message::ChangeColor(r, g, b) => println!("匹配枚举类型的 ChangeColor: r: {}, g: {}, b: {}", r, g, b),
}
// 解构嵌套的枚举 1
enum Message_{
Quit,
Move{x:i32,y:i32},
Write(String),
ChangeColor(Color)
}
enum Color{
Rgb(i32, i32, i32),
Hsv(i32, i32, i32)
}
let msg = Message_::ChangeColor(Color::Hsv(100, 155, 255));
match msg {
Message_::ChangeColor(Color::Rgb(r, g, b)) => println!("匹配到了 Rgb格式, r: {}, g:{}, b:{}", r, g, b),
Message_::ChangeColor(Color::Hsv(h, s, v)) => println!("匹配到了 Hsv格式, h: {}, s:{}, v:{}", h, s, v),
_=>()
}
// 结构体嵌套结构体 2
struct Point{
x:i32,
y:i32
}
let ((a, b), Point{x, y}) = ((1, 2), Point{ x: 3, y: 4 });
println!("a: {}, b: {}, x: {}, y:{}", a, b, x, y);
// _下划线 忽略模式中的值
let f = |_:i32,y:i32|{ println!("在传参的时候忽略了一些值, y: {}", y);};
f(1, 2);
let lis = (1, 2, 3, 4);
match lis{
(one, _, three, _) => println!("one: {}, three: {}",one, three)
}
// 变量名前增加 _ 编译器忽略这个值,只是忽略警告仅此而已, 变量的所有权依旧会被转移
let _x = 5; // 虽然下面没有使用
let y = 4;
let s = Some(String::from("hello")); // 单独的使用 _ 不会转移所有权
if let Some(_) = s{
println!("String")
}
let s = Some(String::from("hello")); // error: borrow of moved value: `s`
if let Some(_sc) = s{
println!("String")
}
// println!("{:?}", s) // 所有权已经被转移
// 使用 .. 忽略一部分值, 使用 .. 匹配的时候不能有 歧义(需要有始有终)
let li = (1, 2, 3, 4, 5);
match li {
(first, .., last) => println!("first: {}, last: {}", first, last)
};
let li = (1, 2);
match li {
(first, ..) => println!("first: {}", first)
};
// 匹配守卫 (用于 match 匹配)
let num = Some(11);
match num {
Some(x) if x < 10 => println!("这个x 小于 10"),
Some(x) if x > 10 => println!("这个x 大于 10"),
Some(x) if x == 10 => println!("这个x 等于 10"),
_ => ()
};
// 匹配守卫 (可以直接使用外部变量, 但是内部变量会沿用自身的)
let num = Some(10);
let y = 10; // 位置1
let x = 12; // 位置2
match num {
Some(x) if x < y => println!("这个x 小于 y"),
Some(x) if x > y => println!("这个x 大于 y"),
Some(x) if x == y => println!("这个x 等于 y"), // 此处的 x 是本作用域的x, y是 位置1 的y
_ => ()
};
// 匹配守卫的优先级 (3 | 4 | 5) if b 优先判断前面匹配, 然后进入匹配守卫
let x = 3;
let b = false;
match x {
3 | 4 | 5 if b => println!("匹配守卫的优先级"),
_ => ()
}
// @ 运算符, 允许我们在创建一个存放值得同时, 测试这个变量是否匹配模式
enum _Message{
Idx { id: i32 }
}
let msg = _Message::Idx { id: 12 };
match msg { // 普通的匹配模式, 匹配成功后结构体内的值进行过滤, 是不能的
_Message::Idx {id:i} => println!("普通的过滤方式无法限制 id 的值 i: {}", i),
}
match msg { // 使用 @ 运算符 可以对结构体/枚举内的值过滤并赋值
_Message::Idx { id: id_value @ 3..=18 } => println!("使用 @ 运算符,匹配到了一些值 id: {}", id_value),
_ => ()
}
}
fn stu29() {
println!("stu29: 不安全的Rust");
// unsafe 并不会关闭借用检查器或禁用任何其他 Rust 安全检查:如果在不安全代码中使用引用,它仍会被检查,unsafe 关键字只是提供了那五个不会被编译器检查内存安全的功能,你仍然能在不安全块中获得某种程度的安全
// 1. 解引用裸指针 2. 调用不安全的函数或方法 3. 访问或修改可变静态变量 4. 实现不安全 trait 5. 访问 union 的字段
// 解引用裸指针, 分为可变和不可变: *mut T, *const T, 这里 * 是类型的一部分, 并不是解引用
// 1. 裸指针允许忽略借用规则, 可以同时拥有多个可变和不可变的指针,或者多个指向相同位置的可变指针
// 2. 不保证指向的内存是有效的
// 3. 允许为空
// 4. 不能实现任何自动清理的功能
// 1. 可以在安全的代码中创建 可变/不可变 裸指针, 只是不能使用和解引用
let mut num = 3;
let r1 = num as *const i32;
let r2 = num as *mut i32;
// println!("r1; {:?}", *r1); // 安全的代码中禁止使用不安全的变量
unsafe {
println!("r1; {:?}", *r1);
println!("r2; {:?}", *r2);
}
// 允许为空 下面可能就是一个空的内存地址
let add = 0x12345usize;
let _r = add as *const i32;
// 调用不安全的函数或者方法
unsafe fn dangerous(){
println!("dangerous fucntion")
};
// dangerous(); // 禁止调用
unsafe {
dangerous()
}
// 创建不安全的代码抽象
fn function_1(){
let mut num = 3;
let r1 = num as *const i32;
let r2 = num as *mut i32;
unsafe {
println!("r1; {:?}", *r1);
println!("r2; {:?}", *r2);
}
}
function_1();
// 调用 c 语言的函数, 属于不安全的代码
extern "C" {
fn abs(input:i32) -> i32;
}
unsafe {
println!("abs(-3) = {}", abs(-3));
}
}
static NUM_STR:&str = "2233";
static mut COUNTER:u32 = 0;
fn stu30() {
println!("stu30: 不安全的可变静态变量");
// 静态变量和常量的区别: 静态变量有固定的内存地址,使用这个值总是访问相同的地址, 常量则允许任何被用到的时候复制其数据
// 静态变量可以是可变的, 虽然可变后是不安全的
// 可变的静态变量 是无法在安全的代码中 读取和修改 的
// 静态变量和程序代码一起被存储于静态存储区中
println!("静态变量, NUM_STR: {}",NUM_STR); // 读取全局的静态变量
// COUNTER += 1; // 禁止在安全的代码中 修改可变静态变量
// println!("COUNTER: {}", COUNTER); // 安全的代码中, 禁止读取 可变静态变量
unsafe {
COUNTER += 1;
println!("COUNTER: {}", COUNTER)
}
}
fn stu31() {
println!("stu31: 不安全的trait");
// 当至少有一个方法包含编译器不能验证的 变量 时, 该 trait 就是不安全的
// 在 trait 之前增加 unsafe 声明为不安全的, 同时 trait 的实现也必须用 unsafe 标记
unsafe trait Say{
fn say(&self);
}
struct Student;
unsafe impl Say for Student{
fn say(&self){
println!("say, say, hello")
}
}
let s1 = Student{};
s1.say()
}
fn stu32() {
println!("stu32: 关联类型");
// 1. 关联类型在 trait定义中 指定占位符类型
// 2. 关联类型是一个 将类型占位符与trait 相关联的方式
// 3. trait 的实现者 会针对特定的实现,在这个类型的指定位置指定相应的具体类型
// 如此可以定义使用多种类型的trait
// trait Iterator{
// type Item;
// fn next(&mut self) -> Option<Self::Item>;
// };
// 如果不使用关联类型, 而是使用泛型那么会出现一些问题, 出现无法推断类型的情况
// 定义泛型迭代器特征
trait Iterator<T>{
fn next(& mut self) -> Option<T>;
}
// 定义迭代器
struct List{
value:i32
}
// 实现迭代器特征 String
impl Iterator<String> for List{
fn next(&mut self) -> Option<String>{
println!("调用了 String 的迭代器");
if self.value <5 {
self.value += 1;
Some(String::from("哈哈"))
}else { None }
}
}
// 实现迭代器特征 i32 类型
impl Iterator<i32> for List{
fn next(&mut self) -> Option<i32>{
println!("调用了 i32 的迭代器");
if self.value <5 {
self.value += 1;
Some(self.value)
}else { None }
}
}
// 下面 使用 next 已经无法推断类型了
let mut l1 = List { value: 1 };
// l1.next(); // 无法推导类型Error: cannot infer type for type parameter `T` declared on the trait `Iterator`
// 使用完全限定语法可以调用上面无法推断类型的迭代器
<List as Iterator<i32>>::next(&mut l1);
<List as Iterator<String>>::next(&mut l1);
}
fn stu33() {
println!("stu33: 默认泛型类型参数和运算符重载");
// 1. 使用泛型类型参数时, 可以为泛型指定一个默认的具体类型
// 2. 运算符重载是指在特定情况下自定义运算符行为的操作
// Rust不允许创建自定义的运算符或者重载运算符, 不过对于std::ops中列出的运算符和相应的 trait, 我们可以使用运算符相关 trait 来重载
use std::ops::Add;
#[derive(Debug)]
struct Point{
x:i32,
y:i32
}
// 重载 Point 的 + 操作
impl Add for Point{
type Output = Point;
fn add(self, other:Point) -> Point{
Point{
x:self.x + other.x,
y:self.y + other.y
}
}
}
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 3, y: 5 };
let p3 = p1 + p2;
println!("p1 + p2 = {:?}", p3);
// 默认泛型类型
// 尖括号里面为默认类型参数, Rhs 是一个 泛型类型参数 right hand side, 这里大写的RHS 是用来指定下方小写rhs的类型的, 这里默认使用了 Self 所以只能和自己本身的类型相加
// RHS和Output,分别代表加法操作符右侧的类型和返回值的类型
// pub trait Add<Rhs = Self> {
// type Output;
// fn add(self, rhs: Rhs) -> Self::Output;
// }
// 修改默认泛型类型, 实现不同结构体相加
// 手动覆盖 Rhs为 其他类型, 因为这是两种不同的结构体 默认的Rhs是self 只能同类型的相加,无法不同类型相加
struct Ms1(u32);
struct Ms2(u32);
impl Add<Ms2> for Ms1{ // 这里 Ms2 就是准备与之相加的结构体
type Output = Ms1;
fn add(self,other:Ms2) -> Ms1{
Ms1(self.0 + other.0)
}
}
let m1 = Ms1(1);
let m2 = Ms2(2);
let m3 = m1 + m2;
// println!("{}",m2.0); // error: value borrowed here after move
}
fn stu34() {
println!("stu34: 完全限定语法");
// Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法,也不能阻止为同一类型同时实现这两个 trait,甚至直接在类型上实现开始已经有的同名方法也是可能的
// 可以让对象按照指定特征运行
// 使用 <Type as trait>::function(), 调用 特定 Type 特定 trait 的方法
// <Type as Trait>::function(receiver_if_method, next_arg, ...);
// 1. 有 self 参数的 同名 方法, 如果特征 和结构体本身的 方法重复, 且有self 参数,那么 在调用方法时 可以自动匹配到对应的方法
// 定义两个特征
trait A{
fn print(&self);
};
trait B{
fn print(&self);
};
// 结构体
struct MyType;
// 特征实现
impl A for MyType{
fn print(&self){
println!("this is A trait")
}
}
impl B for MyType{
fn print(&self){
println!("this is B trait")
}
}
// 本身方法
impl MyType{
fn print(&self){
println!("MyType")
}
}
// 创建实例
let my_type = MyType;
my_type.print(); // 等价于下面的方法
MyType::print(&my_type);
// 调用 MyType 实现了 特征 A/B 的 方法
A::print(&my_type);
B::print(&my_type);
// 2. 完全限定语法 调用没有 self 参数的方法
trait Animal {
fn get_name() -> String;
}
struct Dog;
impl Dog{
fn get_name()->String{
"Dog".to_string()
}
}
impl Animal for Dog{
fn get_name() -> String{
"Animal trait Dog".to_string()
}
}
// 调用 Dog 本身的方法
println!("Dog 本身方法: {}", Dog::get_name());
// 调用 Animal 特征的方法
// println!("Animal trait 的方法: {}", Animal::get_name()); // error: required by `stu34::Animal::get_name`
// 使用完全限定语法调用 指定 trait 的方法
let dog_name = <Dog as Animal>::get_name();
println!("使用完全限定语法: {}", dog_name)
}
fn stu35() {
println!("stu35: 父 trait");
// 类似于为 trait 增加 trait bound
// 类似 "继承", 有时候我们需要某个 trait 使用另一个 trait 的功能, 可以使用 trait trait1:trait2 + trait3 {} 的方法定义 trait
// impl trait1:trait2 + trait3 for T {}, 为所有拥有trait2和trait3行为的类型实现trait1
use std::fmt; // 引入trait
// 定义一个特征, 这个特征也拥有一些继承过来的特征
trait Output: fmt::Display{ // 这里 "继承" fmt::Display
fn out_print(&self){
let output = self.to_string();
println!("out_print: {}", output)
}
}
// 定义一个结构体
#[derive(Debug)]
struct Point{
x:i32,
y:i32
};
// 实现 Output 特征
impl Output for Point{};
// 实现 fmt::Display 特征, 因为这是 Output 特征继承的,如果实现了Output特征, 那么也必须实现继承过来的特征
impl fmt::Display for Point{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
println!("实现了 Display"); // 这里在我本机上明没有打印 奇怪
write!(f, "{}, {}",self.x, self.y)
}
}
let p1 = Point{x:1, y:2};
println!("{:?}",p1)
}
use std::fmt;
use std::sync::Arc;
use std::convert::TryInto;
use std::future::Future;
use std::ops::{Deref, Add};
use std::borrow::{Cow, Borrow, BorrowMut};
use std::any::Any;
use serde_json::value::Value::Array;
use futures::future::{BoxFuture, err};
use rand::{random, Rng};
#[derive(Debug)]
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
write!(f, "({})",self.0.join(", "))
}
}
fn stu36() {
println!("stu36: 孤立规则");
// !! 这里在我本机上 输出并不是 ("小红", "小明") 奇怪
// 孤立规则: 只要 trait 或者类型 对于当前 crate 是本地的话,就可以再此类型上实现该trait
// 如果要实现某个trait,那么该trait和要实现该trait的那个类型至少有一个要在当前crate中定义
// newtype 模式在外部类型上实现外部的trait, 比如一个结构体
// 绕开孤立规则的方法就是使用 newtype 模式
// 使用 Box 脱离孤立规则: Rust 内部使用了一个叫#[fundamental]的属性标识, 使Box 脱离孤儿规则, 源码中可得知; 除了Box<T>,还有Fn、FnMut、FnOnce、Sized
Box::new(123);
let w = Wrapper(vec![String::from("小红"), String::from("小明")]);
println!("{:?}", w);
// 下面如果使用被注释的那行,会报错, is not defined in the current crate, 很明显这里 Option 并不是在本地 crate 实现的 struct, 而是外部标准库引用过来的违反孤立规则在本地不能给它实现特征
// 而再下面一行的 MyData 是本地的所以使用 trati
use std::ops::Add;
struct MyData(i32);
// impl Add<i32> for Option<MyData>{ //
impl Add<i32> for MyData{
type Output =i32;
fn add (self,other:i32)->Self::Output{
(self.0) + other
}
}
}
fn stu37() {
println!("stu37: 动态大小类型");
// Sized标识的是在编译期可确定大小的类型
// Unsize标识的是动态大小类型, 在编译期无法确定其大小, 目前Rust中的动态类型有trait和[T], 只可以通过胖指针来操作Unsize类型,比如&[T]或&Trait
// ?Sized标识的类型包含了Sized和Unsize所标识的两种类型
// 1. 动态大小类型
// 动态大小类型: DST, 这些类型只有在运行的时候才知道大小的类型, 比如 &str 类型
// 动态大小类型的黄金规则: 必须将动态大小类型的值, 置于某种指针之后: 如Box<str>, Rc<str>
// 另一个动态大小类型是 trait, 每一个 trait 都是一个可以通过 trait 名称来引用的 动态大小类型, 为了将 trait 用于 trait 对象, 必须将他们放到指针之后, 比如 &Trait 或 Box<Trait>/Rc<Trait>
// &str 是Unsize, 是一个胖指针,保存了两个值: str的地址和长度. &str的大小在编译时就可以知道,长度是 2*usize
let s1 :&str = "hi";
// 2. Sized trait
// 为了除了 DST, Rust 用Sized trait 来决定一个类型的大小是否是在编译时可知
// 这个 trait 自动为编译器在编译时就知道大小的类型实现
// ?Sized 包括了所有的动态大小类型和所有可确定大小的类型
fn generic<T>(t:T){}; // T 为编译时就自动推导大小和 类型, 和下面等价
fn generic_<T:Sized>(t:T){}; // 和上面等价, T 要求实现 Sized 的特征
// 3. Sized trait 放宽, 对于泛型约束使用 ?Sized, 有可能传入的参数我们也不知道大小, 所以这里需要忽略 T 的大小, 可能是 Sized, 也可能不是 Sized
fn _generic<T: ?Sized>(t: &T){};
}
fn stu38() {
println!("stu38: 高阶函数和闭包");
// 函数指针允许我们 使 函数 作为另一个函数的参数
// 函数的类型是 fn, fn被称为函数指针, 指定参数为函数指针的语法类似于闭包
// 函数指针实现了闭包的三个特征: Fn, FnMut, FnOnce
// Fn代表函数trait object, fn则是函数类型
// 1.
fn add_one(x:i32) -> i32{
x + 1
}
fn utils(func:fn(i32) -> i32, val:i32) -> i32 {
func(val)
}
let num = utils(add_one, 3);
println!("num: {}",num);
// 2. 实现一个泛型修饰函数, 要求传入的第一个参数为 实现了 Fn trait 的类型,
fn wrapper_func<T>(f:T, val:i32) -> i32
where T: Fn(i32) -> i32
{
f(val)
}
fn func(v:i32) -> i32{
v + 1
}
let a = wrapper_func(|x| x * 2, 3); // 使用闭包
let b = wrapper_func(func, 3); // 使用函数指针
// 返回闭包/函数 1
fn return_func_1()-> fn(i32) -> i32{
|x| x + 1
}
let func_1 = return_func_1();
let num_1 = func_1(5);
println!("return_func_1: {}", num_1);
// !! 返回满足了 Fn trait 特征的对象, 使用 dyn 修饰表示这里是满足了 Fn trait 的对象
fn return_f() -> impl Fn(i32) -> i32{ | x:i32 | x }
fn return_func_2() -> Box<dyn Fn(i32) -> i32>{
Box::new( |x| x * x)
}
let func_2 = return_func_2();
let num_2 = func_2(3);
println!("return_func_2: {}", num_2)
}
fn stu39() {
println!("stu39: 宏");
}
fn stu40() {
println!("stu40: From/Into");
// From 和 Into 两个 trait 是内在地联系着的,事实上这是它们的实现的重要 部分:如果能把类型 A 转换成类型 B,那么很容易相信我们也能把类型 B 转换成类型 A
// 你的类型实现了Into From ,那么同时你也就免费获得了 Into
// 使用 Into trait 通常要求指明要转换到的类型,因为编译器大多数时候不能推断转为哪一个类型,因为一个类型可能会有很多 Into
// TryFrom 和 TryInto 是 类型转换的通用 trait;不同于 From / Into 的是, TryFrom 和 TryInto trait 用于易出错的转换,也正因如此,其返回值是 Result 型
// 关于Into有一条默认的规则:如果类型U实现了From<T>,则T类型实例调用into方法就可以转换为类型U, 调用时会把 T当做参数传入U的from方法中
#[derive(Debug)]
struct Number{
val:i32
}
// 为结构体实现 From trait
impl From<i32> for Number{
fn from(val:i32) -> Self{
println!("调用了 from");
Number{val}
}
}
let i = Number::from(123);
println!("i: {:?}",i);
// println!("i: {:?}",i.into()); // error: cannot infer type for type parameter `T` declared on the trait `Into`
// let i_into = i.into(); error: // consider giving `i_into` a type
let i_into_ok:Result<Number,_> = i.try_into(); // 注意!: 使用 try_into 的时候需要给与返回值一个类型,因为编译器无法推导
println!("i_into:Result<Number,_>: {:?}",i_into_ok);
let i_ = Number::from(2233);
let i__into:Number = i_.into(); // 注意!: 使用 into 的时候需要给与返回值一个类型,因为编译器无法推导转为哪一个类型
println!("i__into:Number,_>: {:?}",i__into);
// Complex 实现 From<i32> 后,也可以在 i32 上使用 into 方法,转换到 Complex
#[derive(Debug)]
struct Complex {
re: i32,
im: i32
}
impl From<i32> for Complex{
fn from(re: i32) -> Self {
Complex{
re,
im:0
}
}
}
println!("Hello, world!");
let b: Complex = 1.into(); // 需要手动注明类型, 因为编译器无法自动推导
println!("Complex: {:?}", b);
// 约束泛型时, 用 into()方法 的时候, 会直接把调用者传入 from 中的形参中
fn utils<T>(s:T)
where T:Into<Complex>
{
println!("s.into {:?}",s.into()) // s作为调用者, 被传入到了 from中的参数中了
}
utils(123);
}
fn stu41() -> std::io::Result<()>{
println!("stu41: 网络监听");
use std::net::{TcpListener, TcpStream, SocketAddr, IpAddr};
use std::io::Read;
use std::time;
use std::thread;
// 由于 str 实现了 ToSocketAddrs trait 所以可以直接传入并绑定 ip:port
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
// 接收消息
for msg in listener.incoming(){ // 内部实现了 迭代器 特征可以直接迭代
match msg {
Ok(stream) => client_handle(stream),
_ => {}
}
};
// 对请求的内容进行处理
fn client_handle(mut stream:TcpStream){
// 读取
let mut value:[u8;512] = [0;512]; // 注意这里保存的字节, 需要在下面 转一下类型
stream.read(& mut value).unwrap(); // 使 流读取的内容保存到 value 中
println!("收到了一些内容: {}", String::from_utf8_lossy(&value)); // 使用 from_utf8_lossy 方法把字节转为 String
// 返回
let response = "HTTP/1.1 200 OK \r\n\r\n";
stream.write(response.as_bytes()); // 把response 写入到流中,返回
stream.flush(); // 处理完毕, 刷新流
// 手动睡眠 10秒, 模拟延时操作
thread::sleep(time::Duration::from_secs(10));
}
Ok(())
}
fn stu42() -> std::io::Result<()>{
println!("stu42: 多线程处理 WebServer");
use std::net::{TcpListener, TcpStream, SocketAddr, IpAddr};
use std::io::Read;
use std::time;
use std::thread;
// 由于 str 实现了 ToSocketAddrs trait 所以可以直接传入并绑定 ip:port
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
// 接收消息(这里使用多线程处理)
for msg in listener.incoming(){ // 内部实现了 迭代器 特征可以直接迭代
match msg {
Ok(stream) => {thread::spawn(move ||client_handle(stream));},
_ => {}
}
};
// 对请求的内容进行处理
fn client_handle(mut stream:TcpStream){
// 读取
let mut value:[u8;512] = [0;512]; // 注意这里保存的字节, 需要在下面 转一下类型
stream.read(& mut value).unwrap(); // 使 流读取的内容保存到 value 中
println!("收到了一些内容: {}", String::from_utf8_lossy(&value)); // 使用 from_utf8_lossy 方法把字节转为 String
// 返回
let response = "HTTP/1.1 200 OK \r\n\r\n <!DOCTYPE html>123123";
stream.write(response.as_bytes()); // 把response 写入到流中,返回
stream.flush(); // 处理完毕, 刷新流
// 手动睡眠 10秒, 模拟延时操作
thread::sleep(time::Duration::from_secs(3));
}
println!("结束了");
Ok(())
}
fn stu43() -> std::io::Result<()>{
println!("stu43: 线程池 WebServer");
use std::net::{TcpListener, TcpStream, SocketAddr, IpAddr};
use std::io::Read;
use std::time;
use std::thread;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
// 任务结构体
#[derive(Debug)]
struct Worker{
id:usize,
thread:Option<thread::JoinHandle<()>>
}
// 任务方法: 返回一个任务方法
impl Worker{
// 1.
// fn new(id:usize) -> Self {
// let t = thread::spawn(move || {});
// Worker{id,thread:t}
// }
// fn new(id:usize, receiver:mpsc::Receiver<Job>) ->Self {
fn new(id:usize, receiver:Arc<Mutex<mpsc::Receiver<Message>>>) ->Self { // receiver是一个 一个线程安全的的接收端
let thread = thread::spawn(move || { // 创建一个子线程, 子线程在后台loop循环接收任务
loop{ // 无限循环 接收端 阻塞 接收任务
let message = receiver.lock().unwrap().recv().unwrap(); // 如果收到通道的内容
match message { // 判断通道中内容的类型,是执行新的任务还是停止当前子线程
Message::Run(job)=>{
println!("收到任务: {:?}", id);
job()
}
Message::Stop => { // 如果收到Stop 则立刻停止循环, 下面即执行完毕
println!("结束任务: {}", id);
break
}
}
}
});
Worker{id,thread:Some(thread)} // 返回这个任务到外部主线程
}
}
enum Message{ // 发送给channel中的消息: Run(新增一个任务), Stop(停止该线程)
Run(Job),
Stop
}
// 一个 类型别名, 当做 线程池接收的 函数 的约束
type Job = Box<dyn Send + FnOnce() + 'static>;
// 定义线程池
struct ThreadPool{
// threads:Vec<thread::JoinHandle<()>>
workers:Vec<Worker>,
sender:mpsc::Sender<Message>
}
impl ThreadPool{
fn new(size:usize) -> Self {
assert!(size > 0);
// let mut threads = Vec::with_capacity(size);
// 线程需要传入闭包
// ThreadPool{threads}
let mut workers = Vec::with_capacity(size); // 任务容器
let (sender, receiver) = mpsc::channel(); // 发送端, 接收端
let receiver = Arc::new(Mutex::new(receiver)); // 线程安全的接收端
for id in 0..size{ // 循环 根据 size 创建 子线程, 子线程内接受一个 receiver 接收端, 在线程内循环阻塞接收任务
let worker = Worker::new(id, Arc::clone(&receiver));
println!("创建了子线程: {:?}", worker);
workers.push(worker);
};
ThreadPool{ // 返回创建的线程池
workers,
sender
}
}
fn execute<F>(&self, f:F) // 提交任务
where F:FnOnce() -> () + Send + 'static
{
let job = Box::new(f); // 使用 Box 封装为一个智能指针
self.sender.send(Message::Run(job)).unwrap();
}
}
// 线程池关闭处理
impl Drop for ThreadPool{
fn drop(&mut self){
// 给所有线程发送停止消息(每个线程收到消息后会 break,停止loop接收后续消息了)
for _ in 0..self.workers.len(){
self.sender.send(Message::Stop);
}
// 循环拿出所有的worker, 等待所有的 worker内部 的线程结束
for worker in &mut self.workers{
println!("{:?}", worker);
// worker.thread.join().unwrap(); // Error: cannot move out of `*s` which is behind a mutable reference; 因为worker.thread在 可变引用 之后, 调用这个方法会自动解引用, 未实现Copy trait 禁止解引用
if let Some(thread) = worker.thread.take(){ // 使用 take 拿到Option中的内容,内部补为None, 且不会转移 worker的所有权
thread.join(); // 等待子线程结束
}
}
println!("线程池关闭")
}
}
println!("Server Start");
// 由于 &str 实现了 ToSocketAddrs trait 所以可以直接传入并绑定 ip:port
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
let thread_pool = ThreadPool::new(2);
// 接收连接(这里使用多线程处理), 使用take, 表示只接收10次连接, 10次连接之后,就不再阻塞接收消息, 直接结束程序
for msg in listener.incoming().take(10){ // 内部实现了 迭代器 特征可以直接迭代
match msg {
Ok(stream) => {
thread_pool.execute(|| client_handle(stream)); // 接收到连接之后 提交任务到线程池中
},
_ => {}
}
};
// 对请求的内容进行处理
fn client_handle(mut stream:TcpStream){
// 读取
let mut value:[u8;512] = [0;512]; // 注意这里保存的字节, 需要在下面 转一下类型
stream.read(& mut value).unwrap(); // 使 流读取的内容保存到 value 中
// println!("收到了一些内容: {}", String::from_utf8_lossy(&value)); // 使用 from_utf8_lossy 方法把字节转为 String
// 返回
let response = "HTTP/1.1 200 OK \r\n\r\n <!DOCTYPE html>123123";
stream.write(response.as_bytes()); // 把response 写入到流中,返回
stream.flush(); // 处理完毕, 刷新流
// 手动睡眠 10秒, 模拟延时操作
thread::sleep(time::Duration::from_secs(10));
}
println!("结束了");
Ok(())
}
fn stu44(){
println!("stu44: 读取文件");
use std::fs;
// read 读取字节到 Vector 中
let text = fs::read("./1.txt").unwrap(); // 这里读取到的是 Vec<u8>
println!("读取字节到 Vector 中: {:?}",text);
// 读取字节并转为 String
let text = fs::read_to_string("./1.txt").unwrap();
println!("读取字节并转为 String: {:?}",text)
// 读取目录 read_dir
}
fn stu45() {
println!("stu45: Option 方法");
// 1. take: 把值从Option中拿出来,不转移其所有权,内部留下 None
let mut s: Option<String> = Some("hello".to_string());
let b = s; // 所有权发生了转移
// println!("s: {:?}, b:{:?}", s, b); // Error: value borrowed here after move
let mut s: Option<String> = Some("hello".to_string());
let b = s.take(); // 使用 take 取出内部的值, 而不转移所有权
println!("s: {:?}, b:{:?}", s, b);
// 2. is_some: 当Option中有值得时候, 翻水true
let s: Option<u32> = Some(1);
let b: Option<u32> = None;
println!("s.is_some: {}, b.is_some: {}", s.is_some(), b.is_some());
// 3. is_none 同上, 只不过取反
// 4. contains: 判断内部的值是否相等
// #![feature(option_result_contains)];
// let n = Some(123);
// println!("n.contains(&123): {:?}", n.contains(&123))
// 5. as_ref: 将 &Option<T> 转为 Option<&T>, 把引用本身 转为内部值得引用
// let s = Some("hello".to_string());
// let l = s.map(|x| x.len()); // 如果这样处理会造成所有权转移到闭包内
// println!("s: {:?}, s.as_ref(): {:?}",s, l); Error: value borrowed here after move
let s = Some("hello".to_string());
let l = s.as_ref().map(|x| x.len());
println!("s: {:?}, s.as_ref(): {:?}",s, l);
// 6. unwrap_or, 提供了一个默认值default,当值为None时返回default
}
fn stu46() {
println!("stu46: Queue 队列");
#[derive(Debug)]
struct Queue<T>(Vec<T>, usize);
impl<T> Queue<T>{
// 不限定大小
// fn new() -> Self{
// Queue(Vec::new())
// }
// 限定大小
fn new(size:usize) -> Self {
Queue(Vec::with_capacity(size),size)
}
fn push(&mut self, item:T) -> Result<(), String>{
if self.0.len() == self.1{
println!("没有空间了");
return Err("".to_string());
}
else {
self.0.push(item);
return Ok(())
}
}
fn pop(&mut self) -> Option<T>{
if self.0.len() > 0 {
Some(self.0.remove(0)) // 使用 remove 方法 弹出 指定下标位置元素, 并返回
} else {
None
}
}
fn len(&self) -> usize {
self.0.len()
}
}
let mut q: Queue<i32> = Queue::new(3); // 设置队列最大元素数量
q.push(1);
q.push(13);
q.push(24);
q.push(66);
q.push(74);
q.push(85);
// q.pop(); // 删除一个
println!("q: {:?}, q.len(): {:?} ", q, q.len());
}
fn stu47() {
println!("stu47: Stack 栈结构");
// 定义栈结构体
#[derive(Debug)]
struct Stack<T>(Vec<T>,usize);
impl<T:Debug> Stack<T>{
fn new()->Self{
Stack(Vec::new(), 0)
}
fn push(&mut self, item:T){
println!("入栈了: {:?}", &item);
self.0.push(item);
self.1 += 1;
}
fn pop(&mut self) -> Option<T>{
if self.1 == 0{
return None;
}
self.1 -= 1;
return self.0.pop()
}
fn len(&self) -> usize {
self.0.len()
}
}
let mut stack: Stack<i32> = Stack::new();
stack.push(2233);
stack.push(1234);
stack.push(7878);
stack.pop();
println!("stack 经过出栈入栈: {:?}, 栈深度为: {}", stack.0, stack.1);
}
fn stu48() {
println!("stu48: 链表实现一个栈");
// 定义Node元素
#[derive(Debug)]
struct Node<T>{
value:T,
next:Option<Box<Node<T>>>
}
// 定义栈
#[derive(Debug)]
struct Stack<T>{
top: Option<Box<Node<T>>>
};
impl<T> Stack<T>{
fn new() -> Self{
Stack{
top:None
}
}
fn push(&mut self, value:T) -> (){
// 创建一个节点
let mut node = Node {
next: None,
value,
};
// 新的节点 next应指向曾经给的top节点
let mut top = self.top.take();
node.next = top;
// 新的top 指向刚创建的节点
self.top = Some(Box::new(node));
}
fn pop(&mut self) -> Option<Box<Node<T>>> {
// 修改top 为当前top的next
let mut node = self.top.take(); // 不转移所有权,取出本身top节点内的节点
match node {
Some(mut v) => {
self.top = v.next.take(); // 取出旧node节点中的 next 中的内容, 并把内容赋给栈顶
Some(v) // 返回旧的栈顶 node节点
}
None => None
}
}
}
let mut s = Stack::new();
s.push(1);
s.push(3);
s.push(7);
println!("s: {:#?}", s);
s.pop();
println!("s: {:#?}", s);
s.pop();
s.pop();
s.pop();
println!("s: {:#?}", s);
}
fn stu49() -> io::Result<()>{
println!("stu49: TcpServer");
use std::net::{TcpStream, TcpListener};
use std::time;
use std::io::{Result,self};
use std::thread;
// 监听端口
let listener = TcpListener::bind("127.0.0.1:8080")?;
// 循环读取消息
for stream in listener.incoming(){
println!("创建连接");
// 创建线程处理stream
let stream = stream?;
let handle = thread::spawn(move || stream_handle(stream).expect("处理失败"));
}
fn stream_handle(mut stream:TcpStream) -> io::Result<()>{
let mut content = [0;512];
for _ in 0..1000{
let bytes_read = stream.read(&mut content)?; // 读取内容,并获取读取到的大小
if bytes_read == 0 { // 如果读取的内容为0, 说明读取完毕了
return Ok(())
}
let mut content = String::from_utf8_lossy(&content);
println!("读取到内容: {}, 长度为: {}", content, bytes_read);
stream.write(content.as_bytes());
stream.flush();
thread::sleep(time::Duration::from_secs(1)); // 睡眠1秒
}
Ok(())
}
Ok(())
}
fn stu50() -> io::Result<()> {
println!("stu50: TcpClient");
use std::net::{TcpStream};
use std::time;
use std::io::{Result,self, BufReader};
// 创建连接
let mut stream = TcpStream::connect("192.168.2.34:8080").unwrap();
// 发送10次消息
for i in 0..10{
// 从标准流让用户输入
// let mut msg = String::new();
// io::stdin().read_line(&mut msg);
let mut msg = format!("发送: {}", i).to_string();
// 从当前的流中读取消息(使用 BufReader 提高读取速度)
let mut stream = BufReader::new(&stream);
let mut buffer:Vec<u8> = Vec::new();
stream.read_until(b'\n', &mut buffer).expect("读取失败");
println!("读取的内容: {}", String::from_utf8_lossy(&buffer));
// 发送消息 直接使用 BufReader包裹的stream 写入消息 get_mut() 返回的就是内部的stream
stream.get_mut().write(msg.as_bytes()).unwrap();
stream.get_mut().write("\n".as_bytes());
stream.get_mut().flush();
}
Ok(())
}
fn stu51() -> io::Result<()> {
println!("stu51: UDPServer");
use std::net::UdpSocket;
// 监听端口
let socket = UdpSocket::bind("192.168.2.34:8080")?;
// 循环接收消息
loop {
// 创建缓冲区
let mut buf = [0u8; 512];
// 读取消息, 得到一个读取的大小, 和地址
let (amt, src) = socket.recv_from(&mut buf)?;
// 读取的消息翻转一下
let buf = &mut buf[..amt];
buf.reverse();
// 返回翻转后的消息
socket.send_to(&buf, src)?;
}
Ok(())
}
fn stu52() -> io::Result<()> {
println!("stu52: mio");
// mio是一个异步io库
use mio::net::{TcpStream, TcpListener};
use mio::{Events, Token, Interest, Poll};
// 定义一个 Server
const SERVER:Token = Token(0);
const CLIENT:Token = Token(1);
// 创建一个轮训实例
let mut poll = Poll::new().unwrap();
// 创建事件对象, 最大监听128个事件
let mut events = Events::with_capacity(128);
let addr = "127.0.0.1:8000".parse().unwrap();
// 创建服务端 监听连接
let mut server = TcpListener::bind(addr)?;
// 注册事件 到poll中
poll.registry().register(&mut server,SERVER, Interest::WRITABLE).unwrap();
loop {
// 把事件读到 事件对象中
poll.poll(&mut events, None).unwrap();
// 循环获取事件
for event in events.iter(){
println!("收到连接");
// 匹配事件的 TOKEN 进行分发
match event.token() {
SERVER => {
// 通过事件 的方法 获得事件的 "兴趣"
let is_read = event.is_readable();
let is_write = event.is_writable();
println!("is_read: {}, is_write: {}", is_read, is_write);
// 服务端接收连接
let connect = server.accept();
println!("Server Read");
drop(connect)
}
CLIENT => {
}
_ => {}
}
}
}
Ok(())
}
fn stu53() -> io::Result<()> {
println!("stu53: 序列化/反序列化");
use serde::{Serialize, Deserialize};
#[derive(Debug,Serialize,Deserialize)] // 为结构体显式 的添加相应宏, 使其可以支持序列化以及反序列化操作
struct ServerConfig{
workers:u64,
ignore:bool,
auth_server:Option<String>
}
let config = ServerConfig {
workers: 128,
ignore: true,
auth_server: Some("ServerConfigInstance".to_string()),
};
println!("json格式:");
let serialized = serde_json::to_string(&config).unwrap(); // 序列化操作字符串
let serialized_vec = serde_json::to_vec(&config).unwrap(); // 序列化操作为字节
println!("serialized: {}", serialized);
println!("serialized_vec: {:?}", serialized_vec);
let deserializede:ServerConfig = serde_json::from_str(&serialized).unwrap(); // 反序列化需要标识反序列化之后的类型
println!("deserializede: {:?}", deserializede);
println!("yaml格式:");
let serialized = serde_yaml::to_string(&config).unwrap(); // 序列化操作
println!("serialized: {}", serialized);
let deserializede:ServerConfig = serde_yaml::from_str(&serialized).unwrap(); // 反序列化需要标识反序列化之后的类型
println!("deserializede: {:?}", deserializede);
Ok(())
}
fn stu54(){
println!("stu54: 异步编程1, async基本使用");
use futures::{executor };
use std::thread::sleep;
println!("通过 block_on 阻塞调用");
// 1. 使用 async 关键字, 修饰让 function 变为一个符合 Future trait 的状态机, 调用时可以使用 block_on 阻塞 调用
// 使用 executor::block_on 提交状态机, block_on会阻塞直到Future执行完成
// await也是阻塞执行, 但是会移交线程控制权, 不会阻塞线程
async fn say1(){
println!("async say 1");
sleep(std::time::Duration::from_secs(2));
}
let f1 = say1(); // 返回一个实现了 futures trait 的状态机
executor::block_on(f1);
fn say2(){ println!("say 2");} // 这里由于上面是阻塞用, 所以这里会在调用完 f1 之后才调用
say2();
// 2. 使用await 调用状态机,会阻塞的运行, 但不会阻塞线程,
// await 会以阻塞方式运行, 并交出线程控制权, join! 宏,会并发异步的调用传入内的状态机, 可以等待多个 future 并发完成;
// 通过 await 这一操作, 我们允许 join 宏中的其他任务并发的执行
// 在调用 f_3_4 的时候,内部使用了 await 发生了短暂阻塞, 但是不会阻塞线程, 所以线程转而调用 f5(f5 会接管当前线程)
println!("使用 await 阻塞调用,并交出线程控制权");
async fn say3(){
// 睡眠 3 秒
sleep(std::time::Duration::from_secs(3)); // 注意使用 sleep 会达不到想要的效果,因为这是标准库thread中的sleep 这里会让线程完全阻塞
println!("async say 3");
}
async fn say4(){
println!("async say 4");
}
async fn say5(){
println!("async say 5");
}
// 使用 await 让 say3 和 say4 依照顺序调用, 必须先调用 3, 再调用 4
async fn say_3_4(){
println!("调用say_3_4");
say3().await; // await 会阻塞的方式调用, 并让出线程
say4().await;
}
async fn main_async(){
let f_3_4 = say_3_4();
let f5 = say5();
futures::join!(f_3_4,f5); // 使用 join 调用状态机
}
executor::block_on(main_async()); // 提交状态机
}
fn stu55() {
println!("stu55: 实现一个简单的 future");
use std::thread;
use std::time::Duration;
// 定义Future trait
trait SimpleFuture{
type Output;
fn poll(&mut self, wake:u64) -> Poll<Self::Output>;
}
// 任务状态
enum Poll<T>{
Ready(T),
Pending
}
// 一个类似 future 的结构
struct MySleeper{
polls:u64,
wake:u64
}
impl MySleeper{
fn new(wake:u64) -> Self{
MySleeper{
polls:0, // 循环次数
wake:wake // 序号
}
}
}
// 创建一个任务的状态
static mut STATUS:bool = false;
// 实现 future trait
impl SimpleFuture for MySleeper{
type Output = ();
fn poll(&mut self, wake:u64) -> Poll<Self::Output>{
unsafe{
if STATUS{ // 模拟任务执行完毕
println!("Ready");
Poll::Ready(())
}else { // 模拟执行任务中
self.wake = wake;
self.polls += 1;
println!("Pending");
Poll::Pending
}
}
}
}
struct MyReactor{
wake:u64,
handle:Option<thread::JoinHandle<()>>
}
impl MyReactor{
fn new() -> Self{
MyReactor{
wake:0,
handle:None
}
}
fn register_wake(&mut self, wake:u64){
self.wake = wake;
}
fn check_status(&mut self){
if self.handle.is_none(){
let _wake = self.wake;
let handle = thread::spawn(||{ // 创建一个线程模拟future执行
loop{ // 模拟 一直检测 future 执行状态
thread::sleep(Duration::from_secs(5)); // 模拟 future 执行5秒后完成了任务
unsafe{ // 完成任务后, 模拟 future 执行完毕调用了其 wake唤醒, 改变 Status
STATUS = true;
}
}
});
self.handle = Some(handle);
};
}
}
struct MyExecutor;
impl MyExecutor{
fn block_on<F:SimpleFuture>(mut my_future:F, wake:u64){
loop{
match my_future.poll(wake) {
Poll::Ready(_)=>{ println!("my future is ok"); break}
Poll::Pending => { // 如果正在 Pending 中进入下面不安全的循环体
unsafe{
while !STATUS { // 模拟等待reactor的通知, 循环 STATUS 为false, 如果为 true 说明reactor发现到 future 执行完毕,那么跳出循环, 到外部循环执行future的poll方法
thread::sleep(Duration::from_millis(100));
}
}
}
}
}
}
}
// 创建一个 future
let mut sleeper = MySleeper::new(5);
let wake = sleeper.wake;
// 创建一个reactor
let mut reactor = MyReactor::new();
reactor.register_wake(wake); // 注册
reactor.check_status(); // 开启一个线程进行监听 future执行状态(内部模拟future执行完毕的操作)
// 使用调度器 执行 future
MyExecutor::block_on(sleeper, wake);
}
fn stu56() {
println!("stu56: async/await");
use futures::{executor };
// async 有三种主要的使用方法:async fn,async块和 async闭包, 使用 async 修饰之后的代码块或函数, 变为一个符合 Future trait 的对象
// async 主体中使用的任何变量都必须能够在线程之间传输,因为任何 await 操作都可能导致切换到新线程
// 使用 RC,&RefCell 或任何其他没实现 Send 特征的类型,包括引用未实现 Sync 特质的类型, 是不安全的
// 在future 需要使用 futures::lock 中的 Mutex 而不是 std::sync 中的锁, 来进行数据加锁, 因为使用await是交出线程控制权,使用 std::sync中的锁因为交出了线程控制权所以不会释放锁,其他future在获取锁的时候会导致死锁
// 1. 这个函数为异步调用的函数
async fn say_1() -> u8 {
5
}
// 2. 单独修饰代码块变为 Future trait
fn say_2() -> impl Future<Output=u8>{
async {
5
}
}
// 3. async 生命周期
async fn f1(value:& u8) -> u8{
*value
}
// 上面的例子 等同于下面代码(详见生命周期省略规则), 但是无法编译的: TODO 这里 rust异步编程书 内有坑, 下面这个例子根本无法编译的
// fn f2<'a>(value: &'a u8) -> impl Future<Output=u8> + 'a {
// async {
// *value
// }
// }
// 调用
// fn bad()-> impl Future<Output = u8>{ // 这种调用方式无法编译通过, 因为 x 变量在本函数结束之时就被drop了, 而future返回到了外部,使用的却是被drop的x的引用,造成了生命周期不统一
// let x = 5;
// f1(&x)
// }
fn good() -> impl Future<Output = u8> { // 使用 async 代码块 使其内的 f1 和 变量x 在一个 future内, 这样 内部的x 和f1 是在一个上下文中, 内部的future用多久, 外部的x就存活多久
async {
let x = 5;
f1(&x).await // 使用await 阻塞的运行future, 交出线程控制权, 运行完毕作为返回值, 所以good的签名为 impl Future<Output = u8>, 因为 f1(&x).await阻塞运行直到得到返回值 u8
}
}
}
fn stu57() {
println!("stu57: 异步操作 使用 move");
// 使用move关键字,强制转移其所有权, 就像在线程中那样, 防止垂悬引用
fn move_block(){
let hello = "hello".to_string();
let f = async move { // 强制移动到了 async 块中(async块内实际上是一个future)
let s_ = &hello;
println!("hello: {}",s_)
};
// println!("after move hello: {}",hello) // value borrowed here after move
}
}
fn stu58() {
println!("stu58: join宏");
// 使用futures中的 join! 宏会让传入的 future 并发的执行
// delay_for 可以让future进行阻塞等待, 其本身是个future, 需要await执行
use futures;
use tokio::time::{delay_for, Duration};
use tokio::runtime::Runtime; // 使用 tokio 中的 block_on 的执行器( ps 因为使用了异步库中的 sleep
async fn func1(){
delay_for(Duration::from_secs(3)).await; // 睡眠 3 秒
println!("this is func1");
}
async fn func2(){
println!("this is func2");
}
async fn func_1_2(){
let f1 = func1();
let f2 = func2();
futures::join!(f1, f2);
}
let mut run_time = Runtime::new().unwrap(); // 创建一个执行器
run_time.block_on(func_1_2()) // 按照预期, 先打印 func2, 再打印func1
}
fn stu59() {
println!("stu59: try_join宏");
// 使用try_join, 在执行出错的时候, 会马上返回
// try_join 执行结果也是一个 Result 类型
// try_join 内的future 需要返回 Result 类型
use std::io::Result;
use futures;
use tokio::time::{delay_for, Duration};
use tokio::runtime::Runtime; // 使用 tokio 中的 block_on 的执行器( ps 因为使用了异步库中的 sleep
async fn func1() -> Result<()>{ // 需要返回 Result类型
delay_for(Duration::from_secs(3)).await; // 睡眠 3 秒
println!("this is func1");
Ok(())
}
async fn func2() -> Result<()>{
println!("this is func2");
Ok(())
}
async fn func_1_2(){
let f1 = func1();
let f2 = func2();
let ret = futures::try_join!(f1, f2); // 返回一个 Result 类型
match ret {
Ok(_) => println!("执行成功"),
Err(_) => (),
}
}
let mut run_time = Runtime::new().unwrap(); // 创建一个执行器
run_time.block_on(func_1_2()) // 按照预期, 先打印 func2, 再打印func1
}
fn stu60() {
println!("stu60: select!宏");
// select! 宏会在提交的future 中,任意分支返回即结束运行
// select! 宏中的future必须实现 Unpin trait 和 FusedFuture trait,
// select! 宏内的 future 需要返回 Fuse(Result) 类型, 使用fuse方法, 且注意需要使用 pin_mut宏注册一下
// select 内的future 在执行 完成 一次之后, 该future之后无法再次执行(当然其他的分支不影响)
// select 宏内是按照 future 的可变引用获取的, 通过不获取 future 的所有权,所以在调用select后未完成的future可以继续使用
// select 完成之后不会再轮训 future, 因此需要实现FusedFuture trait 来跟踪 future是否完成 (对应的Stream流里的future也会要求实现对应FusedStream trait)
// select 本身是不会循环的, 可以通过外部的 loop 循环
use futures::{select,future::FutureExt, pin_mut};
use tokio::runtime::Runtime;
use std::io::Result;
async fn func1() -> Result < () > {
tokio::time::delay_for(tokio::time::Duration::from_secs(3)).await; // 睡眠3秒
println!("func1 end");
Ok(())
};
async fn func2() -> Result < () > {
println!("func2 end");
Ok(())
};
async fn async_main() -> Result<()>{
let f1 = func1().fuse(); // 使用 fuse 方法转换
let f2 = func2().fuse();
// 使用 pin_mut
pin_mut!(f1, f2);
// 使用select! 宏(其内任意future执行完毕,立即返回)
select!{
_ = f1 => println!("select f1"),
_ = f2 => println!("select f2"), // f2 执行完毕立即返回了
}
Ok(())
}
let mut run_time = Runtime::new().unwrap();
run_time.block_on(async_main());
}
fn stu61() {
println!("stu61: select!宏中的complete/default");
// 使用 ready 可以创建一个有状态的 future
// select! 宏调度时会可以增加匹配 有complete(期内future都执行完毕,并且不会再次取)和default(都没执行完毕)两个状态
// complete 表示select内future都执行完毕,并且future不会再有相应的后续
// default 表示没有分支完成
use futures;
use futures::{select,executor};
async fn async_main(){
let mut a_fut = futures::future::ready(4i32);
let mut b_fut = futures::future::ready(7i32);
let mut num = 0;
loop {
select!{
a = a_fut => num += a,
b = b_fut => num += b,
complete => {println!("全部执行完毕");break},
default => unreachable!()
};
};
println!("经过了select之后: {}", num);
};
executor::block_on(async_main());
}
fn stu62() {
println!("stu62: future副本");
// 当需要同时运行一个 Future 的多个副本的时候, 可以使用FuturesUnordered类型
}
fn stu63() {
println!("stu63: 返回错误类型");
use std::io::Result;
use futures::executor;
async fn func1(){
// "func1" // 历史问题已经修复: expected `()`, found `&str` 而不是 &'static str 的类型
}
fn func2(){
// "func2" // 和上方错误一样: expected `()`, found `&str`
}
executor::block_on(func1());
// !! 注意在 async 中对错误是可以使用 ? 的
async fn func3() -> Result<()> {
Ok(())
}
async fn func4() -> Result<()> {
let foo = async {
func3().await;
Ok(())
};
foo.await
}
executor::block_on(func4());
}
fn stu64() {
println!("stu64: Send trait");
// 如果一个结构体所有的子类型,都是实现了 Send trait的, 那么其结构体也自动实现 Send trait
#[derive(Default)]
struct NoSend(Rc<()>); // 一个没有实现 Send 的结构体
async fn bar(){};
async fn foo(){
// let x = NoSend::default(); // 这行代码是无法编译的, 因为为foo 创建的future的子类型 x 没有实现 Send trait(Rc不是Send trait)
NoSend::default(); // 这个可以编译成功是因为,该值直接被析构了
{
let x = NoSend::default(); // 这样也是可以编译成功的, 因为x在作用域结束也被析构了, 为foo实现的 future 没有保存这个x
}
let _ = NoSend::default(); // 同样也是直接被析构了,因为使用了忽略参数 _
bar().await;
}
fn run(_:impl Send){};
run(foo())
}
fn stu65(){
println!("stu65: 递归调用 BoxFuture");
// 由于future 递归调用的时候是无法确定其大小的, 可以使用 BoxFuture 一个胖指针包裹
use futures::future::{BoxFuture, FutureExt};
// 注意声明时不需要用 async 声明, 内部返回的 async 块需要使用 boxed方法 转为一个 BoxFuture 返回
fn re() -> BoxFuture<'static, ()>{
async move {
re().await;
}.boxed()
}
}
fn stu_66(){
println!("stu66: self的强制解引用");
// 1. 在未实现Copy trait的struct中使用self
struct S;
impl S{
fn by_ref(&self){}
fn by_mut_ref(&mut self){}
fn by_self(self){}
}
// 拥有所有权 三种方法都可以调用
let mut s = S;
s.by_ref();
s.by_mut_ref();
s.by_self();
// 拥有引用
let mut s = &S;
s.by_ref();
// s.by_mut_ref(); // 这里是无法调用的, 因为签名需要使用的是可变引用
// s.by_self(); // 这里也无法调用, 因为没有实现Copy trait, 所以这里自动解引用获得了所有权,导致外部的变量s成为了野指针
// 拥有可变引用
let mut s = &mut S;
s.by_ref();
s.by_mut_ref();
// s.by_self(); // 这里也无法调用, 因为没有实现Copy trait, 所以这里自动解引用获得了所有权,导致外部的变量s成为了野指针
// 实现了Copy trait的 struct
#[derive(Copy, Clone)]
struct Y;
impl Y{
fn by_ref(&self){}
fn by_mut_ref(&mut self){}
fn by_self(self){}
}
// 拥有所有权
let mut y = Y;
y.by_ref();
y.by_mut_ref();
y.by_self();
// 拥有引用
let mut y = &Y;
y.by_ref();
// y.by_mut_ref(); // 无法使用, 因为这里签名使用的可变借用, 而变量声明时是不可变的
y.by_self(); // 实现了Copy trait的这里解引用之后直接 Copy 一份变量到方法里了
// 拥有可变引用
let mut y = & mut Y;
y.by_ref();
y.by_mut_ref();
y.by_self();
}
fn stu68() {
println!("stu68: Vector 标准库");
// as_slice 返回 Vector的 slice
let v = vec![3, 2, 1, 4, 7];
let v = v.as_slice();
let v = &v[..];
// borrow/borrow_mut 获得 Vector 的不可变/可变 引用, 此方法的返回值需要手动使用变量注解声明
let mut v = vec![3, 2, 1, 4, 7];
let v_borrow:&[i32] = v.borrow();
let v_borrow_mut:&mut [i32] = v.borrow_mut();
// contains 检测 Vector 中是否有指定的值, 返回 bool
let v = vec![3, 2, 1, 4, 7];
let ret = v.contains(&3);
// capacity 返回 Vector的容量 无需重新分配即可容纳的容量
let v:Vec<i32> = Vec::with_capacity(4);
let size:usize = v.capacity();
// chunks 返回一个迭代器, 对 Vector 进行 切块
let v = vec![3, 2, 1, 4, 7];
let mut chunk_iter = v.chunks(2);
let chunk1:Option<&[i32]> = chunk_iter.next(); // Some([3, 2])
let chunk2:Option<&[i32]> = chunk_iter.next(); // Some([1, 4])
let chunk3:Option<&[i32]> = chunk_iter.next(); // Some([7])
println!("{:?}", chunk3);
// clear 清除所有元素, 内部调用的 truncate 方法截断
let mut v = vec![3, 2, 1, 4, 7];
let ret = v.clear();
}
fn stu69() {
println!("stu69: Vector 常用消费器");
// 1. size_hint() 主要用于优化,比如为迭代器的元素保留空间, 返回一个元组内有两个元素: 一个元素表示下限(lower bound),第二个元素表示上限(upper bound), 默认返回 (0, None)
// 1.1 next 会同时消耗上限和下限
let v = [1, 2, 3];
let mut v = v.iter();
v.next();
v.next();
print(v.size_hint()); // (1, Some(1)) 表示元素还剩1个元素
v.next();
print(v.size_hint()); // (0, Some(0))
// 1.2 一个无限的迭代器没有上限, 以及一个最大的下限
print((1..).size_hint());
// 1.3 使用 filter 根据回调,返回回调为True的filter, 也会消耗迭代器本身, 不会消耗上限
let iter = (0..10).filter(|x|x & 2 == 2);
print(format!("使用filter 不会消耗上限: {:?}", iter.size_hint()));
// 2. count 消耗迭代器, 计算迭代次数并返回
let v = [1, 2, 3, 4];
let mut v = v.iter();
println!("count 方法: {:?}", v.count()); // value moved here
// 3. last 消耗迭代器, 并返回最有一个元素(这个方法将对迭代器求值,直到它返回None。在None返回之后,last()将返回它看到的最后一个元素。)
let v = [1, 2, 3];
let mut v = v.iter();
let last = v.last();
println!("使用last 方法获取的最后一个元素为: {:?}",last);
// 4. nth 返回迭代器的第n个元素, 此方法,会消耗之前的元素
let v = [1, 2, 3, 4];
let mut v = v.iter();
let nth_test = v.nth(2); // 得到下标为2个元素
println!("nth 得到了下标为2的元素: {:?}, 迭代器目前状态: {:?}", nth_test, v.size_hint());
// 5. step_by 返回一个新的迭代器,根据步长, 不管给定的步长是什么,迭代器的第一个元素都将被返回
let v = [1, 2, 3];
let mut v = v.iter().step_by(2);
println!("step_by 根据步长进行返回: {:?}", v.next());
println!("step_by 根据步长进行返回: {:?}", v.next());
// 6. chain 链接两个迭代器, 将返回一个新的迭代器,它将首先遍历第一个迭代器的值,然后遍历第二个迭代器的值
let v1 = [1, 2, 3];
let v2 = [2, 3, 4];
let i = v1.iter().chain(v2.iter());
println!("chain 链接了两个迭代器状态为: {:?}", i.size_hint());
// 6.1 由于chain()的参数使用IntoIterator,所以我们可以传递任何可以转换为迭代器的内容,而不仅仅是迭代器本身。例如,slices (&[T])实现了IntoIterator,因此可以直接传递给chain()
let v1 = &[1, 2];
let v2 = &[2, 2, 4, 4, 5];
let mut i = v1.iter().chain(v2);
println!("chain 链接 slice &[T] 状态为: {:?}", i.size_hint());
//7. zip, 将两个迭代器压缩到一个迭代器中, 迭代其他两个迭代器,返回一个元组,其中第一个元素来自第一个迭代器,第二个元素来自第二个迭代器, 一个迭代器返回None, zip将短路,返回 None
let v1 = [2, 2, 3, 4];
let v2 = ['a', 'b', 'c', 'd'];
let mut i = v1.iter().zip(v2.iter());
println!("zip 将两个迭代器进行叠加状态为: {:?}, next的值为: {:?}", i.size_hint(), i.next());
// 7.1 同样 zip 也可以使用 slice &[T], 因为 slice &[T] 实现了 IntoIterator
let v1 = &[1, 2];
let v2 = &['a', 'b', 'c', 'd'];
let mut i = v2.iter().zip(v1.iter());
i.next();
i.next();
i.next();
println!("zip 使用 slice 进行叠加, 迭代之后的值为: {:?}", i.next());
// 8. for_each 主动执行 迭代器的每个元素调用闭包, 类似for循环, 但是比 for 循环要快
let v = [1, 2, 3];
v.iter().for_each(|x|println!("for_each 会为每个元素调用闭包: {}",x));
// 9. map,惰性执行 接收一个闭包, 对迭代器内的每个元素进行调用, 注意! map 是惰性执行的
let mut i = (3..5).map(|x| x * x);
println!("map 会惰性的为迭代器依次调用: {:?}", i.next());
// 10. filter 惰性执行, 过滤(接收一个闭包, 元素传入闭包中,获得返回 True 的元素)
let v = [-1i32, 0, 1, 2, 3, 4];
let mut f = v.iter().filter(|x| x.is_positive()); // 这里判断正数
println!("使用filter 得到 只为正数的 元素, 第一个为: {:?}",f.next());
// 10.1 注意!: iter.filter(f).next() 等价 iter.find(f)
let v = [1, 2, 3];
println!("iter.filter(f).next() 和 iter.find(f), 查找的结果是一样的: {:?}, {:?}", v.iter().filter(|x| x < &&2).next(), v.iter().find(|x| x < &&2));
// 11.filter_map 惰性执行 创建一个迭代器,它在每个元素上调用这个闭包。如果闭包返回一些(元素),则返回该元素, 如果闭包返回None则跳过该元素, 尝试下个元素
let mut v = [1, 2, 3, 4, 5, 6];
let mut i = v.iter().filter_map(|&x| {if x % 2 ==0 {return Some(x)};None});
for c in i{
println!("filter_map 进行联合过滤: {:?}",c)
}
// 12. peekable 创建一个迭代器,它可以使用peek查看迭代器的下一个元素而不消耗迭代器
let mut v = [1, 2, 3, 4, 5].iter().peekable();
println!("peek: {:?}", v.peek());
println!("next: {:?}", v.next()); // 这里依然 是 Some(1)
// 13. skip_while 接收一个闭包, 跳过对闭包返回 true 的元素, 注意!: 闭包在遇到 返回 false 的情况则停止闭包调用, 剩下的元素不再判断
let v = [-1, -2, 4, -7].iter().skip_while(|x| **x < 0);
for i in v{
println!("skip_while 后的结果: {:?}",i)
}
// 14. take 获取指定位置 之前 的元素(不包括指定位置的值), 如果取出的值超过最大元素数量,则只会取到末尾
let v = ['a', 'b', 'c', 'd'];
let i = v.iter().take(3);
for c in i {
println!("使用 take 取之前的值: {:?}", c)
};
// 15. scan 一个类似于fold的迭代器适配器,它保存内部状态并生成一个新的迭代器(接收两个参数, 一个初始的状态初始值, 一个闭包), 迭代器将生成来自闭包的返回值成为一个新的迭代器
let v = [1, 2, 3, 4];
let i = v.iter().scan(1, |status, x| {
*status = *status * x; // 修改状态
Some(*status) // 返回外部
});
for c in i{
println!("scan 为每个元素执行闭包, 并保存相应的状态后: {}", c)
}
// 16. flatten 消除迭代器的中间层
let v = vec![vec![2, 3], vec![9]];
for i in v.iter().flatten(){
println!("flatten: 消除中间层之后: {:?}",i)
}
// 多次调用,可以消除多层
let d3 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]];
let d2 = d3.iter().flatten().collect::<Vec<_>>();
assert_eq!(d2, [&[1, 2], &[3, 4], &[5, 6], &[7, 8]]);
let d1 = d3.iter().flatten().flatten().collect::<Vec<_>>();
assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]);
// 17. flat_map 消除迭代器中间层之前, 为每个元素执行一次map操作, 类似 map(f).flatten()
let v = vec!["abc", "def"];
let i = v.iter().flat_map(|x| x.chars());
for c in i{
println!("{}", c);
}
// 18. by_ref, 使用 by_ref 可以不转移所有权 调用消费器
let mut v = [1, 2, 3, 4].iter();
let sum = v.take(2).fold(0, |status, x| status + x);
println!("一些消费器会转移其所有权,有时候我们不希望被转移所有权: {}", sum);
// println!("这里无法调用, 所有权已经被转移了",v.next());
let mut v = [1, 2, 3, 4].iter();
let sum = v.by_ref().take(2).fold(0, |status, x| status + x);
println!("使用sum 下面依然可以调用: {}", sum);
println!("依然可以调用: {:?}",v.next());
// 19. collect 将迭代器转为集合
let v = ['a', 'b'].iter().collect::<String>();
println!("使用 collect 直接收集为字符串: {}", v);
// 20. partition 根据闭包的规则划分两个新的 迭代器, 一个是闭包返回 true 一个是闭包返回 false 的
let v = [1, 2, 3, 4, 5];
let (even, odd):(Vec<i32>,Vec<i32>) = v.iter().partition(|x| **x % 2 == 0);
println!("使用 partition 分离之后 偶数为: {:?}", even);
println!("使用 partition 分离之后 奇数为: {:?}", odd);
// 21. fold_first 与 fold 相同, 只不过使用 迭代器的第一个元素为初始值, 这是一个 unstable library
let mut i = [1, 2, 3, 4].iter();
// let sum = i.fold_first(|status, x| status + x);
// 22. all 根据闭包调用,如果每个元素调用后都返回 true 那么 all 也就是 true
// 23. any 根据闭包调用,如果其内有一个元素返回了 true 那么 any 也就是 true
// 24. find 根据闭包调用, 返回第一个结果 为 true 的元素
// 25. position 根据闭包调用, 返回第一个结果为 true 的下标
// 26. rposition 从右边开始找, 返回从左到右的下标
// 27. rev 翻转迭代器,调用 next() 的时候是从后往前, 内部实现的 Rev struct 实际上也是调用的 next_back
// 28. sum 对迭代器求和
// 29. eq 判断两个迭代器 其内的元素 是否完全相等
assert_eq!([2,5].iter().eq([2,5].iter()), true); // index 对应的元素也必须相等
// 30. ne 判断两个迭代器内的元素是否不相等
// 31. lt 判断第一个迭代器内的 元素相加 小于 第二个迭代器
assert_eq!([1,3].iter().lt([1,6,3].iter()), true); // 1+3 是小于 1+6+3的
// 32. le 小于等于
// 33. gt: 大于; ge: 大于等于
}
fn stu67() {
println!("stu67: 高阶生命周期");
// 下面的,我运行正常, 暂时没有发现 where for 的应用场景
// 定义一个结构体, 该结构体有一个方法返回本数据的某引用
struct Closure<F>
// where F: Fn(&(i32, i64)) -> &i32
{
data:(i32,i64),
func:F
}
impl<F> Closure<F>
where F: Fn(&(i32, i64)) -> &i32
{
fn call(&self) -> &i32{
(self.func)(&self.data)
}
}
fn func(data:&(i32, i64)) -> &i32{ &data.0};
// 创建一个实例
let clo = Closure { data: (10, 20), func };
println!("{:?}", clo.call());
}
fn main() {
// 1. 变量
// stu1()
// 2. 数据类型
// stu2();
// 3. 函数
// stu3();
// 4. 控制流
// stu4()
// 5. 所有权
// stu5();
// 6. 引用
// stu6()
// 7. slices
// stu7();
// 8. 结构体
// stu8()
// 9. 枚举类型
// stu9()
// 10. vector 类型
// stu10()
// 11. String 类型
// stu11()
// 12. HashMap
// stu12()
// 13. 模块
// stu13();
// 14. 异常
// stu14()
// 15. 泛型
// stu15()
// 16. trait
// stu16()
// 17. 生命周期
// stu17();
// 18. 闭包
// stu18()
// 19. 迭代器
// stu19();
// 20. 智能指针
// stu20();
// 21. Rc智能指针
// stu21();
// 22. RefCell智能指针
// stu22();
// 23. 循环引用/弱引用
// stu23();
// stu24();
// let a = "123";
// let a_Box = Box::new(a);
// let a_Rc = Rc::new(a);
// assert_eq!(*a_Box, *a_Rc)
// 24. 多线程
// stu25();
// Refcell<T>/RC<T> 和 Mutex<T>/Arc<T> 的区别
// 1. Mutex 提供内部的可变性, 类似于 RefCell
// 2. RefCell 和 Rc 是非线程安全的, Mutex, Arc 是线程安全的
// 3. rust中有两个并发概念 std::marker 中的 Sync和 Send trait
// 4.1 通过Send 允许在线程间转移所有权, 任何有Send类型组成的类型,也会自动标记为Send, struct A {a,b,c}如果a,b,c是Send类型那么A也是Send类型的
// 4.1 Send标记 trait 表明类型的所有权可在线程间传递,几乎所有的 Rust类型都可以Send 但是比如 Rc<T>是不能被 Send的
// 4.2 Sync标记 trait 表名一个实现了 Sync类型可以安全的在多个线程中拥有其值得引用, 允许多线程访问, RefCell<T>和Cell<T>都不是 Sync 的, Mutex 是Sync的
// 5. 手动实现 Send 和 Sync 是不安全的
// 25. 对象
// stu26();
// 26. trait 对象
// stu27();
// 27. 模式匹配
// stu28();
// 28. 不安全的 Rust
// stu29();
// 29. 不安全的 static 可变静态变量
// stu30()
// 30. 不安全的 trait
// stu31()
// 31. 关联类型
// stu32();
// 32. 默认泛型类型参数和运算符重载
// stu33();
// 33. 完全限定语法
// stu34();
// 34. 父 trait
// stu35()
// 35. 孤立规则
// stu36();
// 36. never type
// rust中有一个叫做 ! 的特殊类型, 没有值 的类型, 在函数不返回的时候用来当做返回值, 且可以转为其他任意类型
//
fn f1() -> ! {loop{}};
// let never_type = f1();
let a = Some(1);
let num:i32 = match a{
Some(v) => 0,
// None => '1' // `match` arms have incompatible types
None => panic!()
};
// 37. 动态大小类型
// stu37();
// 38. 高阶函数和闭包
// stu38();
// 39. 宏
// stu39()
// 40. From Into
// stu40();
pub fn sum_t(li:& Vec<f64>, mut l:usize) -> f64{
let mut n = li.len();
if n <= 0 || l <= 0 {
return 0 as f64;
}
if n > l{
n = l
}
if n < l {
l = n
}
let sum:f64 = li[l - n..l as usize].iter().sum();
sum
}
println!("{}", sum_t(&vec![1.0, 2.0, 3.0, 4.0], 4));
// fn func1(lis:& Vec<i32>){
// // let a = lis;
// let a = lis;
// let b = &&a;
// let bs = b[0..3];
// }
fn func2(lis: &Vec<i32>){
let ret1 = &lis[..];
let ret3 = &(lis[..]);
let v1 = vec![1, 2, 3];
let v2 = &vec![1, 2, 3];
}
struct A{num:i32};
fn f(r:&mut i32){};
let mut a = A { num: 123 };
let mut mr1 = &mut a;
let mut mr2 = &mut mr1;
let num = mr2.num; // 这里自动解引用了
f(& mut mr2.num);
fn func(args:&& i32){
let t = args;
let t = *args;
}
let s1 = "hello";
let s2 = "hello".to_string();
let s = s1;
println!("{}", s);
let s = s2;
println!("{}", s);
let a = [1, 2, 3];
let l = a.len();
let b = &a;
println!("{:p}",b);
let mut s = "123".to_string();
s.reserve(4);
println!("{:?}",s.len());
// ## 编译时函数执行
const fn get_num()->usize{ 5 };
let arr = [0; get_num()];
// ## for循环与迭代器
let li = [1, 2, 3, 4];
for i in &li {}; // for循环需要借用内的元素
for i in li.iter() {};
// range 类型
let a = 0..100;
// &str 指针和长度
let s = "hello";
println!("as_ptr: {:?}, :p: {:p}", s.as_ptr(), s);
// Box
let a = *Box::new(30);
// ## 在x持有某个对象的所有权的情况下,对x赋值,那么x原来持有的对象就会drop,
// 如果作为函数的返回值, 没有对应的接收者, 那么返回值会立即析构
// 解释2: 就是当x持有某个对象所有权, 如果 x 绑定到了另外一个变量, 那么原来的变量就发生了移动会被加上 drop-flag 标签,在运行时会调用析构函数, 加上 drop-flag的变量意味着生命周期的结束
#[derive(Debug)]
struct Student{name:String};
impl Drop for Student{
fn drop(&mut self){
println!("清理了: {}", self.name)
}
}
// let s1 = Student{name:"s1".to_string()};
// let mut s2 = s1; // 这里 s1 已经变成空变量了
// println!("1");
// s2 = Student{name:"s2".to_string()}; //
// println!("2");
let mut x = Student { name: "x1".to_string() };
let mut y = x; // 这里 x 已经变为一个空的变量了
println!("1");
x = Student { name: "x2".to_string() }; // 对空变量重新赋值
x = Student { name: "x3".to_string() }; // 再次赋值挤走了 x2, 此时x2执行析构函数
x = Student { name: "x4".to_string() }; // 再次赋值挤走了 x3, 此时x3执行析构函数
println!("2");
y = x; // 对 y 重新赋值, y本身保存的是x1, 这时x4 把 x1 挤走, x1被drop了
println!("3");
x = Student { name: "x5".to_string() }; // 空变量x 重新赋值
println!("4");
y = x;
println!("5");
x = Student { name: "x6".to_string() };
println!("6");
y = x;
println!("7");
// ## 如果复合结构本身实现了Drop,则会 先调用它自己的析构函数 后调用其成员的析构函数;否则,如果本身没有析构函数会调用其成员的析构函数
struct Stu1{name:String};
impl Drop for Stu1{
fn drop(&mut self){
println!("清理了: Stu1")
}
}
struct Stu_meta{s:Stu1}
impl Drop for Stu_meta{
fn drop(&mut self){
println!("清理了: Stu_meta")
}
}
// let s1 = Stu1{name:"Stu1".to_string()};
let s_meta = Stu_meta {s: Stu1{name:"Stu1".to_string()}};
{
s_meta;
}
println!("s_meta END");
// ## 较高性能 for循环
for i in vec![(); 100] {}; // 这种更快: 使用单元类型 创建一个长度为 100 的向量
// for i in 0..100 {}; // 这种循环还需要给 i 创建 i32 类型的值
fn ff(a:&str){};
let a = & "hello".to_string();
ff(a); // 解引用多态 传入 & String 也行
// ## 对象安全
trait Stu{ // 定义 trait 对象
fn stu(&self);
}
struct S1;
impl Stu for S1{
fn stu(&self){
println!("trait object call")
}
}
// fn utils(f:& impl Stu){ // 这里已经被包装成 trait 对象了
fn utils(f:& dyn Stu){ // 这里已经被包装成 trait 对象了
// assert_eq!(f, S1);
f.stu(); // 在运行期 先去查虚表, 这里调用的是 trait Object 中虚指针保存的一些信息, 而不是下方传入的 S1的方法
}
utils(&S1); // 传入参数包装成 trait 对象
// ## 对象不安全 this trait cannot be made into an object because method `stu` has generic type parameters
// 这里 trait 中的函数中使用了泛型,在运行时使用了泛型无法确定调用哪个方法
// trait Stu{ // 定义 trait 对象
// fn stu<T>(&self, a:T);
// }
// struct S1;
// impl Stu for S1{ // 这是在编译的时候 在这句代码中创建了 一个 trait object? 那创建的这个 trait object 保存在哪了?
// fn stu<T>(&self, a:T){
// println!("trait object call")
// }
// }
// fn utils(f:& dyn Stu){
// // assert_eq!(f, S1);
// f.stu(1); // 在运行期 先去查虚表, 这里调用的是 trait Object 中虚指针保存的一些信息, 而不是下方传入的 S1的方法
// }
// ## trait 对象专属方法, 使用 impl dyn trait {} 定义
trait Say{
fn say(&self){println!("say")}
}
impl dyn Say{ // 定义 Say 的 trait对象 专有方法
fn say_hello(){println!("say_hello")}
}
struct P;
impl Say for P{};
P.say();
// P.say_hello() // 无法调用, 因为 say_hello 是 Say 的 trait对象 的方法,
Say::say_hello(); // 需要使用 trait::method() 的方式调用
// ## trait 静态分发: 使用 impl
struct Pig;
struct Duck;
trait Fly{
fn fly(&self)->bool;
}
impl Fly for Pig{
fn fly(&self)->bool{false}
}
impl Fly for Duck{
fn fly(&self)->bool{true}
}
fn utils_fly(s:impl Fly){ // 在编译阶段静态分发
s.fly();
}
let p1 = Pig;
utils_fly(p1); // 这样使用更好
// utils_fly::<Pig>(p1);
let d1 = Duck;
utils_fly(d1);
// utils_fly::<Duck>(d1);
// 静态分发 泛型版本
fn utils_fly_t<T:Fly>(s:T){s.fly();}
// ## trait 动态分发 使用 dyn, 这里传入到函数内的 s 就是一个 trait对象 胖指针了,
fn utils_fly_dyn(s:&dyn Fly){
s.fly();
}
// ## String类型实现 Deref 返回的是 &str, 如果一个类型 T实现了Deref<Target=U>,则该类型T的引用(或智能指针)在应用的时候会被自动转换为类型U
// String类型实现的add方法的右值参数必须是&str类型, &s2这里自动是&String类型,会被自动隐式转换为&str
let s1 = "hello".to_string();
let s2 = "world".to_string();
let s3 = s1 + &s2;
// let s3 = s1 + &&&*&&*&&&&s2.deref();
let s4 = s3.deref();
// ## self Self的区别, self代表调用者, Self代表调用者的类型, 这里 Clone::clone 要求返回的需要和 调用者相同类型
// 因为 p 是一个 trait object,我们只知道,它是一个“胖指针”,包括了指向具体对象的指针以及指向虚函数表的指针;p指向的具体对象,它的类型是什么,我们已经不知道了,我们知道的仅仅是,这个类型实现了 Clone trait,其它的就一无所知了;而这个 clone() 方法又要求返回一个与 p 指向的具体类型一致的返回类型;对编译器来说,这是无法完成的任务
// pub trait Clone {
// fn clone(&self) -> Self;
// fn clone_from(&mut self, source: &Self) { ... }
// let p: &Clone = ...;
// let o = p.clone();
// ## Rust中内存对齐: u8占1个字节, u32占4个字节, u16占2个字节; 结构体 Mem_test_1 在对其之后 是 8 个字节
struct Mem_test_1{c:u32, b:u16, a:u8}
println!("Mem_test_1 的内存大小为: {:?}",std::mem::size_of::<Mem_test_1>()); // 8
let mem_instance = Mem_test_1 { a: 1, b: 2, c: 3 };
println!("mem_instance 的内存大小为: {:?}",std::mem::size_of_val(&mem_instance)); // 8
// 联合体以最长成员为对齐数, 联合体中的所有成员都共享一段内存
union Mem_union{c:u32, b:u16, a:u8}
println!("union 的内存大小为: {:?}",std::mem::size_of::<Mem_union>());
// ## Rust内存中重新初始化: 所有权转移之后 对变量重新赋值, 会对其初始化
let mut x = Box::new(123);
let y = x;
x = Box::new(2233);
println!("{:?}", x);
// ## 关联类型约束, (在使用trait 约束泛型的时候, 使用trait内的 关联类型 进行再次约束)
trait MyTrait{
type Flag;
}
struct MyStruct1;
struct MyStruct2;
impl MyTrait for MyStruct1{ type Flag = i32;};
impl MyTrait for MyStruct2{ type Flag = f32;};
fn my_utils<T>(f:T) -> ()
where T: MyTrait<Flag=i32> // 对于实现了 Mytrait 特征的参数, 其内的 Flag 关联类型 也必须为 i32 类型
{}
my_utils(MyStruct1);
// my_utils(MyStruct2); // Error: expected `i32`, found `f32`
// ## 函数指针( 函数指针需要显式指定函数指针类型fn)
fn function()->(){};
let f1 = function; // rust自动推导的无法打印地址说明这不是一个函数指针
// println!("非函数指针: {:p}", f1); // Error: the trait `std::fmt::Pointer` is not implemented for `fn() {main::function}`
let f2: fn()->() = function;
println!("函数指针: {:p}",f2);
// ## Rust中变量的析构顺序是和其声明顺序相反的, 元组内部是按元素的出现顺序依次进行析构的, 结构体内部元素的析构顺序是按排列顺序来析构的
// match 中的map方法, 直接取值, 接收一个闭包对于匹配的变量直接操作并根据业务返回
let s1 = Some(2);
let num = s1.map(|x| x * x);
// catch_unwind 捕捉恐慌(代码传入一个闭包中运行)
use std::panic;
let a = 1;
let b = 0;
let ret = panic::catch_unwind(|| a / b);
match ret {
Ok(x)=>(),
Err(_)=> println!("catch_unwind捕捉恐慌: {:?}", ret)
};
// ## TypeId是一个结构体,其字段t存储了一串数字,这就是全局唯一类型标识符
// TypeId of函数来获取类型T的全局唯一标识符t(可以判断具体类型)
// &dyn Any 可以使用 downcast_ref 可以进行类型推断
use std::any::{Any, TypeId};
let a = "hello".to_string();
println!("对比String的type_id: {:?}, {:?}", a.type_id(), TypeId::of::<String>());
// Any trait 对象 使用(把引用类型的变量转为一个 trait Object, 可以通过 is 判断类型)
let b = &a as & dyn Any;
println!("通过any 的is函数判断类型: {}", b.is::<String>());
let c = b.downcast_ref::<String>().unwrap(); // 再转回来, 转换成 Any 是为了有机会获取到他的类型信息,转换回来,则是为了去使用这个值本身
// ## 类型当做参数传入fn中,(这里泛型 T 仅仅代表的是一个类型, 而没有具体的值, 需要 method::<type> 传入具体 type)
trait Get{
fn check_type<T:Any>(&self) -> bool
where Self:Any
{
TypeId::of::<T>() == self.type_id()
}
}
impl <T:Any> Get for T{};
let s = "hello".to_string();
s.check_type::<String>();
// 两数之和
pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32>{
use std::collections::HashMap;
let mut map: HashMap<i32, i32> = HashMap::new();
for (idx, &num) in nums.iter().enumerate(){
let ret = map.get(&(target - num));
if let Some(f) = ret {
return vec![idx as i32, *f];
}
map.insert(num,idx as i32);
}
vec![0,0]
}
println!("{:?}", two_sum(vec![2,7,11,15],9));
// ## rust 语法细节
let s = "hello".to_string();
s; // 等同于: {s}; let _ = s; 所以会发生所有权转移
// println!("{}", s); // Error: value borrowed here after move
// 41. 网络监听
// stu41();
// 42. 多线程处理 WebServer
// stu42();
// 43. 线程池处理 WebServer
// stu43();
// 44. 文件读取
// stu44();
// 45. Option 方法
// stu45();
// 46. Queue 实现简单队列
// stu46()
// 47. Stack 实现简单栈解构
// stu47();
// 48. 链表实现一个栈
// stu48();
// 49. TcpServer
// stu49();
// 50. TcpClient
// stu50();
// 51. UDPServer
// stu51();
// 52. mio
// stu52();
// 53. 序列化/反序列化
// stu53();
// 54. 异步编程1, 基本使用
// stu54();
// 55. 异步编程2, 实现一个简单的 future
// stu55();
// 56. 异步编程3, async/await
// stu56();
// 57. 异步编程4, move
// stu57();
// 58. 异步编程5, join! 宏
// stu58();
// 59. 异步编程6, try_join 宏
// stu59();
// 60. 异步编程7, select! 宏
// stu60();
// 61. 异步编程8, select! 宏中的complete/default
// stu61();
// 62. 异步编程9, future 副本
// stu62();
// 63. 异步编程10, 返回错误类型
// stu63();
// 64. Send trait
// stu64();
// 65. 异步编程12, 递归调用 BoxFuture
// stu65();
// 66. 关于self相关(自动解引用)
// stu_66();
// 67. 高阶生命周期
// stu67();
// 68. reborrow
// 不可变引用类型的变量是实现了Copy trait 的, 当对一个引用变量赋值给其他变量, 那会Copy 一份到新的变量上 他们共用一个生命周期
// reborrow相关操作,会存在两个可变引用,并不是真的存在两个可变引用, 而是中间发生了所有权转移, 和所有权回归
// 可变引用类型是move 的, 写上注解会让 borrow 变为reborrow
let mut s = "hello".to_string();
let s1 = &mut s;
// let s2 = &mut s; // 有问题! 这里发生了borrow, 违反借用规则(同时存在两个可变借用)
let s2 = &mut *s1; // 没问题! s1所有权 move 到s2 上, 并没有发生借用冲突
// let s2:&mut String = s1; // 没问题! s1 move 到 s2, 使用变量注解
s2; // s2 生命周期到此结束,被drop 所有权返回s1, 如果这两句打印调换一下位置, 则会编译错误,因为违反借用规则
s1; // s1 生命周期到此, 无问题
// 69. 泛型再次研究
// 1.
// T、&T和&mut T都是无限集, 因为可以无限次地借用一个类型;
// T是&T和&mut T的超集;
// &T和&mut T是不相交的集合
trait Trait1 {}
impl<T> Trait1 for T {}
// impl<T> Trait1 for &T {} // compile error 冲突了, 因为 T 已经包含了 &T
// impl<T> Trait1 for &mut T {} // compile error 冲突了, 因为 T 已经包含了 &mut T
trait Trait2 {}
impl<T> Trait2 for &T {} // 没问题 因为是不相交的集合
impl<T> Trait2 for &mut T {} // 没问题
// 2.
// &'static T和T: 'static是不同的
// T : 'static; 应该读作"T被'static lifetime约束"
// T: 'static 可以拥有任意的lifetime, 如果T: 'static,那么T: 'a,因为任意'a均满足'static >= 'a
// T: 'a包括了所有&'a T,但反过来是不成立的。
// T: 'a比&'a T更通用和灵活; T: 'a接受独立类型,含有引用的独立类型,以及引用; &'a T只接受引用
let a = 123;
let b = &a;
println!("{:?}", a);
// 70, impl 与 trait bound, 对于泛型来说, impl 只是一个稍微短的语法, 但是有一个区别, 就是下面的操作 s.check_type::<String>(); impl是无法完成的
trait Get_{
fn check_type_<T:Any>(&self) -> bool
where Self:Any
{
TypeId::of::<T>() == self.type_id()
}
}
impl <T:Any> Get_ for T{};
let s = "hello".to_string();
// 71. Vector 标准库
// stu68();
// 72. iter常用消费器
// stu69();
// 关于内存的理解
let mut s1 = "hello".to_string(); // s1 初始化了里面有个值
let mut s2 = s1; // s1 的值移到了 s2, s1 内的值被移走了
drop(s2); // s2 的值被移走了
s1 = "hi".to_string(); // 再次被初始化了
s2 = "hi".to_string(); // 再次被初始化了
// 实现可 Copy 的类型 只能通过 scope 进行销毁, 满足Copy特质的,位置里面的值无法被移走,换句话说,不存在存储位置存在但是里面的值已经死亡的情况
let i = 123;
drop(i);
{
i; // 对于 实现了 Copy trait的类型, 子作用域显然无法 销毁上级作用域的变量
}
i; // 依然使用
// 实现了 析构行为的 类型, 无法 实现 Copy
// #[derive(Copy, Clone)] // 无法实现 Copy trait
struct Students{
age:i32
}
impl Drop for Students{
fn drop(&mut self){
println!("被释放了: {}",&self.age)
}
}
// 计算程序运行时间
let t = std::time::Instant::now();
for i in 0..100 * 100 * 100 {};
println!("{:?}", t.elapsed());
// 注意 rust 中的类型的方法, 方法内部不允许内部转移类型本身元素发生 move,因为本身的元素发生了move, 那么这个类型就变成"残缺" 的了, 本身可以使用 clone
struct S{
next:String,
current:String
}
impl S{
fn update(&mut self, value:String){
// self.current = self.next; // 这里是禁止的
self.current = self.next.clone();
self.next = value;
}
}
//// 72. result 相关方法
// 72.1 map error: 改变 Err, 接收一个闭包 如果变量为err, 则err的值使用闭包调用的结果, 就是将 Result<T, E> 变为 Result<T, F>
let f = |x: i32|->String{ format!("出错了: {}", x)};
let o:Result<i32,i32> = Ok(123);
let o_ret: Result<i32, String> = o.map_err(f);
println!("o_ret: {:?}", o_ret);
let e:Result<i32,i32> = Err(2233);
let e_ret: Result<i32, String> = e.map_err(f);
println!("e_ret: {:?}", e_ret);
// 72.2 and 对两个Result 进行组合, 如果x是ok 那么就返回y, 如果x不ok 那么就返回x本身的Err
let x: Result<i32, i32> = Ok(0);
let y: Result<i32, i32> = Err(1);
println!("x.and(y): {:?}", x.and(y)); // Err(1) 这里x是ok的,这里返回了y的值
let x: Result<i32, i32> = Err(0);
let y: Result<i32, i32> = Ok(1);
println!("x.and(y): {:?}", x.and(y)); // Err(0) // 这里x是Err的 返回x本身的Err
let x: Result<i32, i32> = Err(0);
let y: Result<i32, i32> = Err(1);
println!("x.and(y): {:?}", x.and(y)); // Err(0) // 这里x是Err的 返回x本身的Err
let x: Result<i32, i32> = Ok(0);
let y: Result<i32, i32> = Ok(1);
println!("x.and(y): {:?}", x.and(y)); // Ok(1) // 这里x是ok 的, 这里返回了y的值
// 72.3 and_then, 基于结果值的回调, x.and_then(|x| {...}), 如果x是ok的, 那么x结果进入一个闭包可以再次处理, 如果不ok 那么返回x的Err
// 72.4 unwrap_or, 提供了一个默认值default,当值为None时返回default
let x: Result<i32, _> = Err(2233);
println!("x.unwrap_or: {}", x.unwrap_or(7788));
// 生命周期缺陷
// #[derive(Debug)]
// struct Foo;
// impl Foo{
// fn mutate(&mut self)->&Self{&*self}
// fn share(&self){}
// }
//
// let mut foo = Foo;
// let loan = foo.mutate();
// foo.share();
// println!("{:?}",loan) // error 这里无法编译, 明明是不可变引用,但是这里却变成了可变引用
// 读取 环境变量
#[derive(Debug)]
struct B{name:String};
#[derive(Debug)]
struct C{age:i32};
trait tb{};
trait tc{};
fn fb(f:B){
println!("{:?}", f.name);
};
fn fc(f:C){
println!("{:?}", f.age);
};
// fn func_(...){
// ...
// }
//
// func_(fb);
// func_(fc);
}
|´・ω・)ノ
师傅,收徒弟吗?( ๑´•ω•) "(ㆆᴗㆆ)୧(๑•̀⌄•́๑)૭
不错 当日记来翻翻,到此一游!୧(๑•̀⌄•́๑)૭
感谢分享!