我是靠谱客的博主 文艺铃铛,最近开发中收集的这篇文章主要介绍Rust语法、特性自学笔记,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述


main.rs
// 基于对 https://kaisery.github.io/trpl-zh-cn/title-page.html 的学习理解,整理本文档,用于个人回忆复习。
// 好读书,不求甚解。特别复杂的特性,可能以后永远也用不上的,就没做整理。性价比低。
// 通读全文完成rust语法、特性复习。
// 其中以“_”开头的变量命名只是为了不产生告警。
// 关于工作空间、Crate和模块管理、自动化测试、集成测试、外部包使用、自建包使用、与C语言互相调用等内容,见project_solution
mod closure;
mod collection;
mod common;
mod config;
mod const_var;
mod expression;
mod function;
mod genericity;
mod if_and_loop;
mod iterator;
mod lifetime;
mod memory;
mod option_result;
mod ownership;
mod pattern_mode;
mod process;
mod remark;
mod smart_pointer;
mod the_enum;
mod thread_usecase;
mod types;
mod unit_test;
mod unsafe_rust;
mod use_string;
mod use_struct;
mod use_trait;
// 进程入口,返回值类型默认为元组()
fn main() {
()
}
closure.rs
use super::common::*;
// 闭包的基本用法
#[test]
fn use_closure_case1() {
let func1 = |num| println!("hello! {}", num);
func1(10);
}
// 闭包的写法
#[test]
fn use_closure_style() {
// 这个是一个内嵌的函数,不是闭包
fn add_one_v1(x: u32) -> u32 {
println!("result is param + 1");
x + 1
}
// 闭包写法1:完整标注
let add_one_v2 = |x: u32| -> u32 {
println!("result is param + 1");
x + 1
};
// 闭包写法2:省略返回类型,自动推断
let add_one_v3 = |x| {
println!("result is param + 1");
x + 1
};
// 闭包写法3,自动推断
let add_one_v4 = |x| x + 1;
assert_eq!(add_one_v1(1), 2);
assert_eq!(add_one_v2(1), 2);
assert_eq!(add_one_v3(1), 2);
assert_eq!(add_one_v4(1), 2);
}
#[test]
fn use_closure_case2() {
// 这里num的类型因为下面func1(10)被自动推导成i32
let func1 = |num| {
println!("hello! {}", num);
num
};
let _result = func1(10);
// 编译失败:expected integer, found floating-point number
//let _result = func1(10.0);
assert_eq!(get_var_type(_result), "i32");
assert_eq!(
get_var_type(func1),
"hello_rust::closure::use_closure_case2::{{closure}}" // 闭包类型
);
let func2 = |param| {
println!("hello! {}", param);
param
};
let _result = func2(10.0);
assert_eq!(get_var_type(_result), "f64");
assert_eq!(
get_var_type(func2),
"hello_rust::closure::use_closure_case2::{{closure}}" // 闭包类型
);
assert_eq!(
get_var_type(&func2),
"&hello_rust::closure::use_closure_case2::{{closure}}" // 闭包引用类型
);
}
// 闭包的复制
#[test]
fn use_closure_case3() {
let x = 0;
let func1 = |num| num;
let y = 0;
let funcx = func1; // 这里是复制,而不是Move
let z = 0;
assert_eq!(func1(10), 10);
assert_eq!(funcx(10), 10);
// 此闭包占用了4字节栈上内存
println!("{:p}
", &x); // 0x8263fd2 cc
println!("{:p}", &func1); // 0x8263fd2 d0 +4
println!("{:p}
", &y); // 0x8263fd2 d4 +4
println!("{:p}", &funcx); // 0x8263fd2 d8 +4
println!("{:p}
", &z); // 0x8263fd2 dc +4
}
// 闭包占用栈上内存
#[test]
fn use_closure_case4() {
let x = 0;
let func1 = |num| num;
let func2 = |num| num;
let func3 = |num1, num2| num1 + num2;
let y = 0;
assert_eq!(func1(10), 10);
assert_eq!(func2(10.0), 10.0);
assert_eq!(func3(10, 10), 20);
// 说明闭包的实际代码段不存储在栈上
// 这里猜测闭包实现其实是一个结构体
// 然后这个结构体实现了闭包trait
println!("{:p}
", &x); // 0x2052afd5 0c
println!("{:p}", &func1); // 0x2052afd5 10 +4
println!("{:p}", &func2); // 0x2052afd5 18 +8
println!("{:p}", &func3); // 0x2052afd5 20 +8
println!("{:p}
", &y); // 0x2052afd5 24 +4
}
// 泛型闭包写法
#[test]
fn use_generic_closure1() {
struct _Cacher<T>
where
T: Fn(u32) -> u32, // 注意:这里是泛型约束,而不是具体类型
// 要求实际类型必须为一个入参和返回值都是u32的闭包或函数
{
_calculation: T,
_value: Option<u32>,
}
// 也可以这么定义非泛型版本:
// struct _Cacher2 {
//
_calculation: Box<dyn Fn(u32) -> u32>,
//
_value: Option<u32>,
// }
// 还可以使u32也成为泛型类型
// struct _Cacher3<T, U>
// where
//
T: Fn(U) -> U,
// {
//
_calculation: T,
//
_value: Option<U>,
// }
impl<T> _Cacher<T>
where
T: Fn(u32) -> u32,
{
// 实现一个缓存方法
fn _get_value(&mut self, x: u32) -> u32 {
if let Some(result) = self._value {
return result;
}
let result = (self._calculation)(x); // 这里必须这么加括号
self._value = Some(result);
result
}
}
// 实例化一个cacher
let mut _cacher1 = _Cacher {
_calculation: |num| num + 1, // 这里确定了实际类型,就是本匿名类型的闭包类型
_value: None,
};
assert_eq!(
get_var_type(&_cacher1),
"&hello_rust::closure::use_generic_closure1::_Cacher<hello_rust::closure::use_generic_closure1::{{closure}}>"
);
assert_eq!(_cacher1._get_value(10), 11); // 直接执行一把闭包
assert_eq!(_cacher1._get_value(1), 11); // 直接返回了缓存的值
assert_eq!(_cacher1._get_value(2), 11); // 直接返回了缓存的值
assert_eq!(_cacher1._get_value(1000), 11); // 直接返回了缓存的值
}
// 所有的闭包都实现了 trait Fn、FnMut 或 FnOnce 中的一个
// 原型如下,最主要的区别:捕捉环境变量的方式
// Fn 从其环境获取不可变的借用值
// pub trait Fn<Args>: FnMut<Args> {
//
fn call(&self, args: Args) -> Self::Output;
// }
// FnMut 获取可变的借用值所以可以改变其环境
// pub trait FnMut<Args>: FnOnce<Args> {
//
fn call_mut(&mut self, args: Args) -> Self::Output;
// }
// FnOnce 消费从周围作用域捕获的变量
// 其名称的 Once 代表了闭包不能多次获取相同变量的所有权的事实,所以它只能被调用一次
// 由于所有闭包都可以被调用至少一次,所以所有闭包都实现了 FnOnce
// pub trait FnOnce<Args> {
//
type Output;
//
fn call_once(self, args: Args) -> Self::Output;
// }
// 使用闭包捕获上文变量
// 闭包定义的那一行就捕获了
// 当闭包从环境中捕获一个值,闭包会在闭包体中储存这个值以供使用。这会使用内存并产生额外的开销
#[test]
fn use_closure_get_env_var1() {
let x: i32 = 88;
// 编译报错:函数无法捕获变量
// can't capture dynamic environment in a fn item, use the `|| { ... }` closure form instead
// fn function1(param: i32) -> bool {
//
x == param
// }
// 闭包直接使用x变量
let closure1 = |param: i32| -> bool {
println!("i can use x");
x == param
};
assert_eq!(closure1(88), true);
assert_eq!(closure1(87), false);
}
// 大部分需要指定一个 Fn 系列 trait bound 的时候,可以从 Fn 开始
// 而编译器会根据闭包体中的情况告诉你是否需要 FnMut 或 FnOnce。
#[test]
fn use_closure_get_env_var2() {
struct _Point {
_x: i32,
_y: i32,
};
let param1 = _Point { _x: 0, _y: 1 };
// 定义闭包的时候,就已经把param1的所有权Move给闭包了
// 这里标注move,说明本闭包是一个夺取了所有权的FnOnce 类型的闭包
let closure_fn_once = move || {
println!("{}-{}", param1._x, param1._y);
};
// 可以连续调用,因为都是执行一个闭包,param1的所有权已经在里面了
closure_fn_once();
closure_fn_once();
// println!("{}", param1._x);
// 编译报错:borrow of moved value: `param1`, variable moved due to use in closure
// 因为param1的所有权已经进到闭包closure_fn_once里了,所以这里不能再用了,报错
// 这里不知道closure_fn_mut是什么类型的
// 总之不是FnOnce的,因为没有显示move,猜测是FnMut的
let mut param2 = _Point { _x: 0, _y: 1 };
let mut closure_fn_mut = || {
param2._x += 1;
param2._x
};
assert_eq!(closure_fn_mut(), 1);
assert_eq!(closure_fn_mut(), 2);
assert_eq!(closure_fn_mut(), 3);
assert_eq!(closure_fn_mut(), 4);
assert_eq!(closure_fn_mut(), 5);
// 这里还能用param2,说明其所有权没有被夺取
println!("{}-{}", param2._x, param2._y);
}
// 返回闭包
#[test]
fn return_closure() {
// fn returns_closure() -> Fn(i32) -> i32 {
//
|x| x + 1
// }
// 编译报错:
// trait objects without an explicit `dyn` are deprecated
// 说明这个返回值闭包类型,本质上是一个trait
// 像对待trait一样,对待闭包
fn _returns_closure() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
}
collection.rs
use super::common::*;
// i32切片,其实同&str
#[test]
fn i32_slice_usecase() {
let arr = [1, 2, 3, 4, 5];
let slice = &arr[1..3]; // slice的ptr指向栈上内存地址
assert_eq!(get_var_type(slice), "&[i32]"); // 切片类型
assert_eq!(slice[0], arr[1]);
assert_eq!(slice[1], arr[2]);
// 切片访问也会越界
// assert_eq!(slice[2], arr[3]); // 运行异常:index out of bounds: the len is 2 but the index is 2
// 下面两行组合起来,编译失败。原理参考string_slice_usecase,arr变化过之后,原来的切片就失效了
// arr[1] = 200;
// assert_eq!(slice[0], 200);
// 不可变本体,不能获取其可变切片
// let arr = [1, 2, 3, 4, 5];
// let slice = &mut arr[1..3];
// 编译报错,cannot borrow `arr` as mutable, as it is not declared as mutable
// 总之编译器会保证同一时间,同一作用域只有一个可变引用或本体修改行为(本体修改行为会通过再生成一个可变引用实现)。下面代码报错。
let mut arr = [1, 2, 3, 4, 5];
let slice = &mut arr[1..3];
// println!("{}", arr[0]); // 编译报错:cannot use `arr` because it was mutably borrowed
println!("{}", slice[0]);
}
// Vec 基本用法1
#[test]
fn use_vec_base1() {
let mut vec1: Vec<i32> = Vec::new(); // 假如没有下文,编译器推断不出vec1的类型,所以需要加类型注解
vec1.push(1);
vec1.push(2);
vec1.push(3);
vec1.push(4);
let out_val = vec1.pop(); // pop返回Option<T>,pop带所有权
assert_eq!(out_val, Some(4));
assert_eq!(get_var_type(out_val), "core::option::Option<i32>");
assert_eq!(vec1[0], 1);
assert_eq!(&vec1[0], &1);
assert_eq!(get_var_type(&vec1[0]), "&i32");
assert_eq!(vec1.get(0), Some(&1)); // 注意,这里get(index)获取的是Some(&T),get不带所有权
assert_eq!(get_var_type(vec1.get(0)), "core::option::Option<&i32>");
assert_eq!(vec1.get(100), None);
let vec2 = vec![1, 2, 3];
assert_eq!(vec1, vec2);
assert_eq!(vec1.pop(), Some(3));
assert_eq!(vec1.pop(), Some(2));
assert_eq!(vec1.pop(), Some(1));
assert_eq!(vec1.pop(), None);
assert_eq!(vec1.pop(), None);
assert_eq!(vec1.len(), 0);
assert_eq!(vec1.capacity(), 4);
let mut vec3 = vec![String::from("zhang xiao san"), String::from("li si")];
// let _my_name = vec3[0]; // 编译失败: cannot move out of index of `std::vec::Vec<std::string::String>`
let _my_name = &mut vec3[0]; // 可变引用
*_my_name = String::from("zhang xiao er");
let _my_name2 = &vec3[0]; // 不可变引用
assert_eq!(get_var_type(_my_name2), "&alloc::string::String");
assert_eq!(vec3.len(), 2);
assert_eq!(vec3.capacity(), 2); // cap == 2
println!("{:?}", vec3); // ["zhang xiao er", "li si"]
变过了 没有Move
} // <- 这里 vec1 vec2 vec3 离开作用域并被丢弃(free),vec3内的2个元素也会被free
#[test]
fn use_vec_base2() {
let v = vec![1, 2, 3, 4, 5];
let first = &v[0];
// v.push(6);
// 上一行push编译报错,cannot borrow `v` as mutable because it is also borrowed as immutable
// rust编译器保证了first的绝对可用,因为push的时候,可能发生relloc,导致first失效。
// 所有如果后续用到了first,就不允许push
// 如果没有下一句使用first的地方,就允许push
assert_eq!(get_var_type(first), "&i32");
}
#[test]
fn use_vec_base3() {
let mut arr = vec![100, 101, 102];
// arr不会失去所有权
for item in &arr {
assert_eq!(get_var_type(item), "&i32");
}
// arr不会失去所有权
for item in &mut arr {
*item += 1; // ++
assert_eq!(get_var_type(item), "&mut i32");
}
// arr不会失去所有权
for item in &mut arr {
assert_eq!(get_var_type(item), "&mut i32");
// 编译报错,value used here after move
// 因为get_var_type(item)已经把item的所有权拿走并且释放了
// 即: &mut i32变量,遵循所有权规则
// *item += 1;
}
// arr不会失去所有权
for item in &mut arr {
assert_eq!(get_var_type(&item), "&&mut i32");
func(item); // ++
func(item); // ++
}
fn func(pa: &mut i32) {
*pa += 1;
}
assert_eq!(&arr, &[103, 104, 105]);
// arr失去了所有权
for item in arr {
assert_eq!(get_var_type(item), "i32");
println!("{}", item); // 103 104 105
}
// println!("{:?}", arr); // 编译报错,arr已经失去了所有权
}
// Vec存储不同类型数据
#[test]
fn use_vec_base4() {
#[derive(Debug)]
enum SpreadsheetCell {
Int(i32),
Bint(u128), // 128/8=16
Float(f64),
}
let e1 = SpreadsheetCell::Int(3);
assert_eq!(get_val_size(&e1), 24); // 24字节,1->8 + 16 = 24,其中1->8可能为字节对齐,也可能本来就是8
let e2 = SpreadsheetCell::Bint(2);
let e3 = SpreadsheetCell::Float(10.12);
let vec = vec![e1, e2, e3]; // e1 e2 e3的所有权给了arr
println!("{:?}", vec); // [Int(3), Bint(2), Float(10.12)]
assert_eq!(get_val_size(&vec), 24); // 猜测 8(p) + 8(len) +8(cap) = 24
// &e1编译报错
// move occurs because `e1` has type `use_vec_base4::SpreadsheetCell`, which does not implement the `Copy` trait
// assert_eq!(get_val_size(&e1), 24);
// 枚举数组大小符合内存预期
let e1 = SpreadsheetCell::Int(3);
let e2 = SpreadsheetCell::Bint(2);
let e3 = SpreadsheetCell::Float(10.12);
let arr = [e1, e2, e3]; // e1 e2 e3的所有权给了arr
assert_eq!(get_val_size(&arr), 72); // 128 / 8 = 16
16 + 8 = 24
24 * 3 = 72
}
// Vec结构体需要栈内存 28byte
#[test]
fn vec_macro_self_need_mem() {
let x = 100;
let y = 1000;
let arr = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, x, y];
let z = 100;
// 内存合理
println!("
{:p}", &x); // 0xe96576da 10
println!("
{:p}", &y); // 0xe96576da 14 +4
println!("{:p}", &arr); // 0xe96576da 18 +4
println!("
{:p}", &z); // 0xe96576da 34 +28
}
// 双端队列
#[test]
fn use_double_queue() {
use std::collections::VecDeque;
let mut queue = VecDeque::new();
queue.push_back(1); // 1
queue.push_back(2); // 1 2
queue.push_front(0); // 0 1 2
queue.push_front(-1); // -1 0 1 2
let mut iter = queue.into_iter();
assert_eq!(iter.next(), Some(-1));
assert_eq!(iter.next(), Some(0));
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), None);
}
// 双向链表
#[test]
fn use_linked_list() {
use std::collections::LinkedList;
let mut list = LinkedList::new();
list.push_back(1);
list.push_back(2);
list.push_front(0);
list.push_front(-1);
}
#[test]
fn use_hash_map1() {
use std::collections::HashMap;
let mut scores = HashMap::new();
let color1 = String::from("Blue");
let color2 = String::from("Red");
// 插入键值对
scores.insert(color1, 10);
scores.insert(color2, 50);
// 一旦键值对被插入后就为哈希 map 所拥有
// 对于像 i32 这样的实现了 Copy trait 的类型,其值可以拷贝进哈希 map
// 对于像 String 这样拥有所有权的值,其值将被移动而哈希 map 会成为这些值的所有者
// println!("{}", color1); // 编译报错:borrow of moved value: `color1`
// 传入键,获取值
let team_name = String::from("Blue");
let score = scores.get(&team_name);
assert_eq!(score, Some(&10));
assert_eq!(get_var_type(score), "core::option::Option<&i32>"); // get返回类型
let val = scores["Blue"];
assert_eq!(val, 10);
assert_eq!(get_var_type(val), "i32"); // []返回类型
// 下面一行,通过不存在的key获取值,直接panic
// let val = scores["xxxxx"]; // note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
// 遍历键值对
for (key, value) in &scores {
assert_eq!(get_var_type(key), "&alloc::string::String");
assert_eq!(get_var_type(value), "&i32");
println!("{}: {}", key, value);
// Blue: 10
// Red: 50
}
for (key, value) in &mut scores {
*value += 1; // ++
assert_eq!(get_var_type(key), "&alloc::string::String");
assert_eq!(get_var_type(value), "&mut i32");
}
println!("{:?}", scores); // {"Red": 51, "Blue": 11}
for (_, value) in &mut scores {
*value -= 1; // --
}
println!("{:?}", scores); // {"Red": 50, "Blue": 10}
// 覆盖、替换值
scores.insert(String::from("Blue"), 11); // 覆盖值,消耗掉key的所有权
scores.insert(String::from("Blue"), 25); // 覆盖值,消耗掉key的所有权
println!("{:?}", scores); // {"Red": 50, "Blue": 25}
// 检查key,or_insert: 如果不存在key,就插入值
scores.entry(String::from("Yellow")).or_insert(1); // 检查map中没有Yellow,则插入了{"Yellow": 1}
scores.entry(String::from("Blue")).or_insert(1000); // 检查map中已有Blue,则不插入
println!("{:?}", scores); // {"Red": 50, "Blue": 25, "Yellow": 1}
// map中是否存在某个key
assert_eq!(scores.get("heheda"), None); // 不存在
if scores.get("heheda") == None {
println!("score not have heheda");
}
// 使用Entry更新值
let ent = scores.entry(String::from("Yellow")).or_insert(0); // 因为存在键Yellow,所有不会执行or_insert(0)
*ent += 1;
assert_eq!(get_var_type(ent), "&mut i32");
println!("{:?}", scores); // {"Red": 50, "Blue": 25, "Yellow": 2}
}
// 自定义哈希
// 使自己定义的类型,可以作为HashMap的key类型
#[test]
fn use_custom_hash() {
use std::collections::HashMap;
#[derive(Hash, PartialEq, Eq)]
struct Point {
x: i32,
y: i32,
z: i32,
}
let mut dict = HashMap::new();
dict.insert(Point { x: 0, y: 1, z: 2 }, 0);
dict.insert(Point { x: 2, y: 1, z: 2 }, 2);
dict.insert(Point { x: 3, y: 1, z: 2 }, 3);
let mut key = Point { x: 0, y: 1, z: 2 };
assert_eq!(dict.get(&key), Some(&0));
key.x = 2;
assert_eq!(dict.get(&key), Some(&2));
key.x = 3;
assert_eq!(dict.get(&key), Some(&3));
key.x = 4;
assert_eq!(dict.get(&key), None);
}
common.rs
// 获取类型名, 入参_可于编译时确认泛型T的的实际类型
pub fn get_var_type<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
// 获取值占用内存大小
pub fn get_val_size<T>(val: &T) -> usize {
std::mem::size_of_val::<T>(val)
}
config.rs
// 发布配置(release profiles)
// 运行 cargo build 时采用的 dev 配置
//
dev 配置被定义为开发时的好的默认配置
// $ cargo build
//
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
// 运行 cargo build --release 时采用的 release 配置
//
release 配置则有着良好的发布构建的默认配置
// $ cargo build --release
//
Finished release [optimized] target(s) in 0.0 secs
// 覆盖dev默认配置:
// [profile.dev]
// opt-level = 0
// 覆盖release默认配置:
// [profile.release]
// opt-level = 3
const_var.rs
use super::common::*;
// 全局常量声明
// 亮点:必须带具体类型,编码时可大胆使用,无需担忧隐式类型转换规则可能导致的各种问题
const MAX_U32: u32 = 0xffffffff - 1; // 字面值不可越界,支持编译时可确认值的常量表达式
const MAX_I8: i8 = 127; // 不可越界,=128则编译失败
const PI: f64 = 3.1415926535897932384668765413265764651313654;
const MY_NAME: &str = "zhang xiao san"; // 字符串常量,字符串字面量存储方式类似C语言
// 类似全局常量的函数,会在编译期执行,只支持常数计算
const fn make_const_five() -> u32 {
let mut result = 10;
result += 1;
result -= 1;
result + 2 - 2
}
// 使用全局常量
#[test]
fn use_global_constant() {
// Rust不支持类似C99的变长数组VLA(variable-length array)
// 但是下面调用函数make_const_five()允许作为数组长度,说明编译器以常量来对待const fn function()函数
let arr = [0.5f32; make_const_five() as usize];
assert_eq!(get_var_type(arr), "[f32; 10]");
// 全局常量地址不那么连续
println!("global const
MAX_U32 address {:p}", &MAX_U32); // 0x7ff6facaf 8b8
println!("global const
MAX_I8 address {:p}", &MAX_I8); // 0x7ff6facaf 908 +80
println!("global const
PI address {:p}", &PI); // 0x7ff6facaf 960 +88
println!("global const MY_NAME address: {:p}", &MY_NAME); // 0x7ff6facaf 9c0 +96
println!("make_const_five add:{:p}", &make_const_five()); // 0x36b6f6d7bc 地址说明本质上是函数,只不过是编译时执行。
}
// 修改全局常量值
#[test]
fn change_global_const() {
println!("change_global_const");
// 不能获取全局常量引用,只能得到一个u32,而不是&mut u32
// 以下是带告警的代码:
// let max_u32: &mut u32 = &mut MAX_U32;
// println!("global const MAX_U32 value {}", max_u32); // 4294967294
// assert_eq!(get_var_type(max_u32), "u32");
// 告警信息:
// note: each usage of a `const` item creates a new temporary
// note: the mutable reference will refer to this temporary, not the original `const` item
}
// 使用局部常量
#[test]
fn use_local_constant() {
// 局部常量定义
const MIN_INT8: i8 = -128;
const MAX_INT8: i8 = 127;
// 局部常量地址也不那么连续,且与局部变量地址不挨着
let x = 0;
println!("local car x address:
{:p}
", &x); // 0x233791f7a4 栈上的地址
println!("local const MIN_INT8 address: {:p}", &MIN_INT8); // 0x7ff701676 6ba
println!("local const MAX_INT8 address: {:p}", &MAX_INT8); // 0x7ff701676 500 -442
}
expression.rs
// 大括号{}会创造新作用域。同时,{}自身是一个表达式,会返回值
#[test]
fn brace_is_expr() {
let y = {
let x = 3;
x + 1
};
assert_eq!(y, 4);
}
// 看似正确,其实编译报错的代码
#[test]
fn expr_err() {
// fn get_val(val: i32) -> i32 {
//
while true {
//
return val + 1;
//
}
// }
// 以上函数报错:expected `i32`, found `()`
// 因为while可真可假,所以实际上,没有最终返回值
// 人能识别出 while true,但是编译器不能
}
// 使用println!格式化输出
#[test]
fn use_println() {
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
let p = Point { x: 12, y: 2048 };
println!("{:?}", p); // Point { x: 12, y: 2048 }
println!("{:b}", 255); // 11111111 二进制
println!("{:o}", 255); // 377 八进制
println!("{:x}", 255); // ff 小写十六进制
println!("{:X}", 255); // FF 大写十六进制
println!("{:e}", 255); // 2.55e2 小写指数
println!("{:E}", 255); // 2.55E2 大写指数
let x = 0;
println!("{:p}", &x); // 指针
}
function.rs
// 函数定义展示:
fn _add_function1(x: i32, y: i32) -> i32 {
// panic!("!!!");
return x + y; // 函数返回值
}
// 函数定义展示:
fn _add_function2(x: i32, y: i32) -> i32 {
x + y // 函数返回值,注意:不带分号
}
// 函数内嵌套定义函数
#[test]
fn function_in_function() {
fn func1() -> () {
println!("func1");
()
}
func1();
func2();
// 嵌套定义的函数,位置无要求
fn func2() {
println!("func1");
() // 无返回值的函数,默认返回值类型为()
}
}
// 函数指针
#[test]
fn use_function_ptr() {
fn add_one(x: i32) -> i32 {
x + 1
}
// 注意参数f的写法
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
// 可传函数
let answer = do_twice(add_one, 5);
assert_eq!(answer, 12);
// 也可以传闭包
let answer = do_twice(|num| num + 1, 5);
assert_eq!(answer, 12);
}
genericity.rs
use super::common::*;
// 泛型基础:
#[test]
fn genric_function_test1() {
// 获取切片中的最大值
// 返回类型 &T 引用
// T: 必须实现PartialOrd,否则无法使用 < 进行比较
fn _get_max_value1<T: PartialOrd>(slice: &[T]) -> &T {
let mut result = &slice[0]; // 这里有可能panic!
for elem in slice.iter() {
if result < elem {
result = elem;
}
}
result
}
// 返回类型 T
// T 必须实现PartialOrd且实现Copy,否则不能<比较,不能使用slice[index],因为会发生Move
fn _get_max_value2<T: PartialOrd + Copy>(slice: &[T]) -> T {
let mut result = slice[0]; // 这里有可能panic!
for &elem in slice.iter() {
if result < elem {
result = elem;
}
}
result
}
// 和 _get_max_value2 函数签名一致,使用where更短
// 和 C# 相似
fn _get_max_value3<T>(slice: &[T]) -> T
where
T: PartialOrd + Copy,
{
slice[0]
}
}
// 泛型结构体1
#[test]
fn genric_struct_test1() {
struct Point<T> {
_x: T,
_y: T,
}
let _integer = Point { _x: 5, _y: 10 }; // genric_struct_test1::Point<i32>
let _float = Point { _x: 1.0, _y: 4.0 }; // genric_struct_test1::Point<f64>
// let wont_work = Point { _x: 5, _y: 4.0 };
// 编译报错 4.0 expected integer, found floating-point number
// 因为如果定义结构体,要求x和y类型必须一致,都是T
}
// 泛型结构体2
#[test]
fn genric_struct_test2() {
struct Point<T, K> {
_x: T,
_y: K,
}
let _1 = Point { _x: 5, _y: 10u8 };
assert_eq!(
get_var_type(_1),
"hello_rust::genericity::genric_struct_test2::Point<i32, u8>"
);
let _2 = Point { _x: 5, _y: 4.0 };
assert_eq!(
get_var_type(_2),
"hello_rust::genericity::genric_struct_test2::Point<i32, f64>"
);
let _3 = Point {
_x: "zhang san",
_y: String::from("li si"),
};
assert_eq!(
get_var_type(_3),
"hello_rust::genericity::genric_struct_test2::Point<&str, alloc::string::String>"
);
}
// 泛型方法
#[test]
fn genric_function_test2() {
struct _Point<T> {
x: T,
y: T,
}
// 这里 impl 必须带T,且和_Point带同样的类型
// 必须在 impl 后面声明 T,这样就可以在 Point<T> 上实现的方法中使用它了
// 在 impl 之后声明泛型 T ,这样 Rust 就知道 Point 的尖括号中的类型是泛型而不是具体类型
impl<T> _Point<T> {
fn _get_x(&self) -> &T {
&self.x
}
}
// 选择为 Point<f32> 实例实现方法
// 而不是为所有泛型 Point 实例
impl _Point<f32> {
fn _distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
let _p1 = _Point { x: 1, y: 2 };
// 下行编译错误: no method named `_distance_from_origin` found for struct
// let _dis = _p1._distance_from_origin();
// 下面的不报错
let p2 = _Point {
x: 1.0f32,
y: 2.0f32,
};
let _dis = p2._distance_from_origin();
}
// 复杂、灵活,可能永远都用不上的泛型方法
#[test]
fn genric_function_test3() {
struct Point<T, U> {
x: T,
y: U,
}
// impl后必须加<T, U>,保证编译器认为T U是泛型,而不是类型
impl<T, U> Point<T, U> {
// 方法自带泛型<V, W>
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}
let p1 = Point { x: 5, y: 10.4 };
assert_eq!(
get_var_type(&p1),
"&hello_rust::genericity::genric_function_test3::Point<i32, f64>"
);
let p2 = Point { x: "Hello", y: 'c' };
assert_eq!(
get_var_type(&p2),
"&hello_rust::genericity::genric_function_test3::Point<&str, char>"
);
let p3 = p1.mixup(p2); // p1 p2 Move进去,free了
assert_eq!(p3.x, 5);
assert_eq!(p3.y, 'c');
assert_eq!(
get_var_type(&p3),
"&hello_rust::genericity::genric_function_test3::Point<i32, char>"
);
}
if_and_loop.rs
use super::common::*;
// if基本用法
#[test]
fn if_basic_usecase() {
// 基本用法,不带括号
let c = 'a';
// if里面必须是bool类型,不能是整形,指针等
// 亮点:简单暴力,bool原生类型,读代码再也不用分析某些人写C语言,类似if(!NULL)写法的意思了,少转弯,可读性好
if c == 'a' {
println!("c is a");
} else if c == 'b' {
println!("c is b");
} else {
println!("c is not a and not b");
}
// 用于组合成复杂求值表达式,此时if+{}+else+{}是一套表达式,整体会返回一个值
let x = if c == 'a' {
println!("return 500");
500
} else {
println!("return 400");
400 // 500 和 400 必须类型一致,否则无法推断x的类型
};
assert_eq!(x, 500);
assert_eq!(get_var_type(x), "i32");
}
// 循环loop基本用法,loop类似C语言的while(true),但属于表达式
#[test]
fn loop_basic_usecase() {
let mut x = 1;
loop {
x += 1;
if x >= 10 {
break; // 表达式结果:空元组()
}
}
// 作为表达式
let x = loop {
let tmp = rand::random::<i32>();
if tmp > 1 {
break tmp; // 表达式结果
}
};
assert_eq!(x > 1, true);
assert_eq!(get_var_type(x), "i32");
}
// 循环while基本用法,和其他语言一致。
// 可break;可continue;但不能像loop一样作为表达式
#[test]
fn while_basic_usecase() {
let mut i = 0;
while i != 1000000 {
if i % 2 == 0 {
i += 1;
continue;
}
if i >= 1000 {
break;
}
i += 1;
}
}
// 循环for基本用法
#[test]
fn for_basic_usecase() {
let a = [10, 20, 30, 40, 50];
let mut i = 0;
// 这里有一个模式匹配,如果不加&,那么element的类型就是&i32
// 下面这种写法,element的类型是i32
for &element in a.iter() {
assert_eq!(a[i], element);
i += 1;
assert_eq!(get_var_type(i), "usize"); // i用于索引,被自动推导成usize
}
// 1..4 是一个Range类型,默认闭合状态为:[1..4)
// Range本身实现了迭代器trait接口,所以可以这么用
for number in 1..=4 {
// "1..=4"相当于[1..4],首尾都是闭合的
println!("for_basic_usecase: {}", number); // 1 2 3 4
}
// rev() 是Range的一个方法,返回一个实现了迭代器的对象
for number in (1..4).rev() {
println!("for_basic_usecase: {}", number); // 3 2 1
}
}
iterator.rs
use super::common::*;
// 迭代器基本用法
#[test]
fn use_iterator1() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter(); // fn iter(&self) -> Iter<'_, T> 这里迭代器拥有一份v1的不可变引用
assert_eq!(get_var_type(&v1_iter), "&core::slice::iter::Iter<i32>"); // 迭代器属于核心库
for val in v1_iter {
println!("Got: {}", val);
assert_eq!(get_var_type(val), "&i32");
}
// use of moved value: `v1_iter`
// 在前一个for循环中,v1_iter就已经被消耗了
// 注意一个模式匹配问题:本val类型为i32
// for &val in v1_iter {
//
println!("Got: {}", val);
//
assert_eq!(get_var_type(val), "i32");
// }
}
// Vec<T>的方法可以生成三种迭代器:
#[test]
fn impl_of_vec_iterator1() {
// 不可变引用迭代器trait定义:
// pub trait Iterator {
//
type Item; // trait 的 关联类型,Item 类型将是迭代器返回元素的类型
//
fn next(&mut self) -> Option<Self::Item>; // next() 是 Iterator 实现者被要求实现的唯一方法
//
Iterator trait拥有大量的带默认实现的函数
//
只要实现了next(),就是Iterator了,就能用这些函数了
// }
// 动态数组iter()方法返回的迭代器的使用效果:
// 当迭代器结束时,next()返回 None
let v1 = vec![1, 2, 3];
let mut v1_iter = v1.iter();
// Vec<>的iter()方法返回一个包含其不可变引用的迭代器
assert_eq!(get_var_type(&v1_iter), "&core::slice::iter::Iter<i32>");
assert_eq!(v1_iter.next(), Some(&1));
assert_eq!(v1_iter.next(), Some(&2));
assert_eq!(v1_iter.next(), Some(&3));
assert_eq!(v1_iter.next(), None);
// Vec<>的iter_mut()方法返回一个包含其可变引用迭代器
let mut v1 = vec![1, 2, 3];
let v1_iter_mut = v1.iter_mut();
assert_eq!(
get_var_type(&v1_iter_mut),
"&core::slice::iter::IterMut<i32>"
);
for elem in v1_iter_mut {
*elem += 1;
}
assert_eq!(v1[0], 2);
let v1 = vec![1, 2, 3];
// Vec<>的into_iter()方法返回一个获取其所有权的迭代器
let v1_iter_omner = v1.into_iter();
assert_eq!(get_var_type(&v1_iter_omner), "&alloc::vec::IntoIter<i32>");
// 这个循环中,直接获取了动态数组元素的所有权
for elem in v1_iter_omner {
assert_eq!(get_var_type(elem), "i32");
}
// 编译报错:borrow of moved value: `v1`
// move occurs because `v1` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
// assert_eq!(v1[0], 2);
}
// 消耗迭代器
#[test]
fn cost_iterator1() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
// sum方法是Iterator默认实现的一个方法,把自身消耗了
let total: i32 = v1_iter.sum(); // fn sum<S>(self) -> S
assert_eq!(total, 6);
}
// 迭代器适配器,迭代器适配层
// 这个类似C#的Linq特性,爱!
// 所有的迭代器都是惰性的
#[test]
fn iterator_adaptors() {
let v1: Vec<i32> = vec![1, 2, 3];
// 如果不进行collect会有警告:unused `std::iter::Map` that must be used
// 调用 map 方法给迭代器加上一个适配层
// map相当于LinQ中的Select<>()
// 接着调用 collect 方法消费迭代器+适配层,并创建一个新动态数组
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
assert_eq!(get_var_type(&v2), "&alloc::vec::Vec<i32>");
assert_eq!(v2[0], 2);
}
// 使用filter创建过滤适配层,捕获环境的闭包
#[test]
fn iterator_adaptors2() {
// 鞋子结构体定义
// PartialEq 使Shoe间可以用 == 进行比较
#[derive(PartialEq, Debug)]
struct Shoe {
size: u32,
style: u8,
}
// 获取入参所有权
fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
// into_iter 生成一个获得动态数组每个元素所有权的迭代器
// filter 按照指定的规则过滤(闭包返回false的被过滤掉、忽略掉)
// filter 相当于给原始迭代器加上一个适配层
// 闭包内直接使用了上下文变量:shoe_size
// collect 把经过过滤的元素重新组成一个动态数组,其中过滤返回false的就丢弃了
shoes.into_iter().filter(|s| s.size == shoe_size).collect()
}
let shoes = vec![
Shoe { size: 10, style: 0 },
Shoe { size: 13, style: 1 },
Shoe { size: 10, style: 2 },
];
let in_my_size = shoes_in_my_size(shoes, 10);
assert_eq!(
// 因为这里如果断言失败,需要打印,所以必须实现Shoe的{:?} Debug trait
in_my_size,
vec![Shoe { size: 10, style: 0 }, Shoe { size: 10, style: 2 },]
);
}
// 实现一个自己的不可变引用迭代器,提供遍历1-5个数
#[test]
fn impl_my_simgple_iterator() {
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
impl Iterator for Counter {
// 注意这里的写法,表示迭代器内元素定死了,就是u32
type Item = u32;
// 这里的返回类型实际上是: Option<u32>
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
// 这里制造一个迭代器
let mut counter = Counter::new();
assert_eq!(counter.next(), Some(1));
assert_eq!(counter.next(), Some(2));
assert_eq!(counter.next(), Some(3));
assert_eq!(counter.next(), Some(4));
assert_eq!(counter.next(), Some(5));
assert_eq!(counter.next(), None);
// 实现迭代器trait,成为迭代器对象,要求必须实现自己特有的next()方法
// 实现后,就可以使用拥有默认实现的 Iterator trait 的方法了
// a.zip(b):生成一个迭代器适配层,生成一个二元组
// zip的 a b分别是两个迭代器,其实,不要求a b他们俩的next()的返回类型一致
// zip 在任一输入迭代器返回 None 时也返回 None,所以一共只有4对儿元素
// skip(1):跳过一个迭代元素,即执行一把next(),把结果丢掉
// map的时候,由a里有五个元素,而b因为skip(1),只有四个元素,所以一共只有4对儿元素
// map里a,b 分别指前一个迭代器的元素和后一个迭代器的元素,分别对俩迭代器使用next()来获取的
// map的结果是把俩迭代器的next()元素值进行计算,生成一个值,在这里map实际上叠加了一个迭代器适配层
// filter 过滤,再加一层适配层
// sum,消耗迭代器+多层适配层
// 业务逻辑:Counter 实例产生的值,将这些值与另一个 Counter 实例在省略了第一个值之后产生的值配对
//
将每一对值相乘,只保留那些可以被三整除的结果,然后将所有保留的结果相加,
let sum: u32 = Counter::new()
.zip(Counter::new().skip(1))
.map(|(a, b)| a * b)
.filter(|x| x % 3 == 0)
.sum();
assert_eq!(18, sum);
}
lifetime.rs
// 生命周期注解:并不改变任何引用的实际变量的生命周期的长短
// 生命周期注解:要求 编译器 必须按照所标注的要求 进行生命周期检查
// 生命周期注解:主要用来描述函数/方法的参数与返回值之间的生命周期规则
// &i32
// 普通引用
// &'a i32
// 带有显式生命周期的引用
// &'a mut i32 // 带有显式生命周期的可变引用
#[test]
fn show_lifetime_case1() {
// 'a是生命周期的泛型类型。
// 生命周期标注和泛型T类似,一般默认用'a、'b,就像泛型用T U一样
// 约束:x、y、返回值,三者的生命周期一样:都是'a
// 'a的实际值 = x y 两者中,最早释放的那个入参的生命周期
fn _longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
// 下面这一组写法报错:
// `string2` does not live long enough
// 这里编译器的逻辑是这样的:
// 1、通过分析调用者代码,编译器明白'a的实际值就是string2的生命周期(因为string2死的最早)
// 2、因为返回值也被标注了'a,所以:必须保证《result只在string2死之前有效》
// 3、但是很明显,string2死后,仍然使用了result,那只好报错了
// 4、根据第2条得知,如果把打印result那一行删掉,就不报错了
//
因为即便result死的比string2晚,但是string2死掉后,没人再用result了,也就不会发生问题
// let string1 = String::from("long string is long");
// let result;
// {
//
let string2 = String::from("xyz");
//
result = _longest(string1.as_str(), string2.as_str());
// }
// println!("The longest string is {}", result);
}
// 结构体的生命周期
#[test]
fn show_lifetime_case2() {
struct ImportantExcerpt<'a> {
_part: &'a str,
}
let novel = String::from("Call me Ishmael. Some years ago...");
// first_sentence引用了novel
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
// _part的生命周期同first_sentence
let _i = ImportantExcerpt {
_part: first_sentence,
};
// 生命周期标注意义:_i实例只在novel死之前有效
}
// 生命周期规则:所有引用是有生命周期的。
// 生命周期规则:包括函数和方法的入参和返回值,只要是引用类型,同样都必须有明确的生命周期
// 大多数时候,编译器会根据一定规则推算出各引用的生命周期,此时是不需要程序员标注的
// 生命周期 自动推断一共有三条规则,可以综合使用
// 如果编译器使用了三条规则,仍旧不能判断函数和方法所有引用型入参和返回值的生命周期,则编译报错,需手工标注
// 生命周期 自动推断
// 规则1: 所有函数的引用型 入参 都有它自己独特的生命周期
#[test]
fn show_lifetime_auto_rule_1() {
// 写法1:
fn _foo1_0(_x: &i32) {}
// 自动推断效果1:
fn _foo1_1<'a>(_x: &'a i32) {}
// 写法2:
fn _foo2_0(_x: &i32, _y: &i32) {}
// 自动推断效果2:
fn _foo2_1<'a, 'b>(_x: &'a i32, _y: &'b i32) {}
// 写法3:
fn _foo3_0(_x: &i32, _y: &i32, _z: &i32) {}
// 自动推断效果3:
fn _foo3_1<'a, 'b, 'c>(_x: &'a i32, _y: &'b i32, _z: &'c i32) {}
}
// 规则2: 如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数
#[test]
fn show_lifetime_auto_rule_2() {
// 写法1:
fn _foo1_0(x: &i32) -> &i32 {
x
}
// 自动推断效果1:
fn _foo1_1<'a>(x: &'a i32) -> &'a i32 {
x
}
// 写法2:
fn _foo2_0(x: &i32) -> (&i32, &i32, &i32, &i32) {
(x, x, x, x)
}
// 自动推断效果2:
fn _foo2_1<'a>(x: &'a i32) -> (&'a i32, &'a i32, &'a i32, &'a i32) {
(x, x, x, x)
}
}
// 规则3: 如果方法有多个输入生命周期参数并且其中一个参数是 &self 或 &mut self,那么所有输出生命周期参数被赋予 self 的生命周期
// 规则3解读1:必须是个方法而不是关联函数
// 规则3解读2:self不能以获取所有权的方式进来,必须通过引用方式作为第一个入参
// 规则3解读3:如果方法除了第一个&self入参外,还有其他引用型入参,则参考规则1
// 规则3解读4:如果方法返回值中没有引用类型,则此规则不起作用
#[test]
fn show_lifetime_auto_rule_3() {
// 空结构体定义
struct _Node {}
impl _Node {
// 不适用于本规则,因为self不是引用型的
fn _func1(self) {}
// 不适用于本规则,没输出参数
fn _func2(&self) {}
// 不适用于本规则,输出参数不是引用型的
fn _func3(&self) -> i32 {
0
}
// 写法1
fn _func4_0(&self) -> &str {
"hehe"
}
// 自动推断效果1:
fn _func4_1<'a>(&'a self) -> &'a str {
"hehe"
}
// 写法2
fn _func5_0(&self) -> (&str, &str, &str) {
("hehe", "hehe", "hehe")
}
// 自动推断效果2:
fn _func5_1<'a>(&'a self) -> (&'a str, &'a str, &'a str) {
("hehe", "hehe", "hehe")
}
// 写法3
fn _func6_0(&self, _x: &str) -> (&str, &str, &str) {
("hehe", "hehe", "hehe")
}
// 自动推断效果3:
// 注意入参_x的生命周期是'b
fn _func6_1<'a, 'b>(&'a self, _x: &'b str) -> (&'a str, &'a str, &'a str) {
("hehe", "hehe", "hehe")
}
// 所以以下写法第一次_x处报错:
// this parameter and the return type are declared with different lifetimes...
// fn _func6_2(&self, _x: &str) -> (&str, &str, &str) {
//
(_x, _x, _x)
// }
}
}
// 静态生命周期
#[test]
fn show_static_lifetime() {
// 'static,其生命周期能够存活于整个程序期间
// 所有的字符串字面值都拥有 'static 生命周期
let _s: &'static str = "I have a static lifetime.";
let _s = "I have a static lifetime.";
}
memory.rs
use super::common::*;
// 关于内存问题
// 对于开发者来说,只需要掌握类型系统、所有权系统、混合编程范式即可。
// 无需操心底层内存安全
// 信任编译器!
// 局部变量地址
#[test]
fn local_var_address() {
let x = 0;
let y = 1;
let z = 2;
// 局部变量的地址是连续的
println!("x address: {:p}", &x); // 0x6fb451f5 5c
println!("y address: {:p}", &y); // 0x6fb451f5 60 +4
println!("z address: {:p}", &z); // 0x6fb451f5 64 +4
}
// println!宏占用栈上空间
#[test]
fn println_use_stack_memory() {
let x = 0;
println!("x address: {:p}", &x); // 0x6e420fd4 4c 栈上基础地址
let y = 1;
println!("y address: {:p}", &y); // 0x6e420fd4 a4 +58
let z = 2;
println!("z address: {:p}", &z); // 0x6e420fd4 fc +58
}
struct _EmptyStruct {}
// 空结构体实例、数组、空元组及其数组占用空间大小
#[test]
fn empty_struct_use_mem_space() {
let a = _EmptyStruct {};
println!("{}", get_val_size(&a)); // 0 空结构体实例占用空间0
// let an = a; // 编译报错 value used here after move,因为空结构体实例,也是遵循所有权规则的
let b = ();
println!("{}", get_val_size(&b)); // 0 空元组占用空间0
let c = (_EmptyStruct {}, _EmptyStruct {}, _EmptyStruct {});
println!("{}", get_val_size(&c)); // 0 空结构体元组占用空间0
assert_eq!(
get_var_type(c),
"(hello_rust::memory::_EmptyStruct, hello_rust::memory::_EmptyStruct, hello_rust::memory::_EmptyStruct)"
);
let arr = [
_EmptyStruct {},
_EmptyStruct {},
_EmptyStruct {},
_EmptyStruct {},
];
println!("{}", get_val_size(&arr)); // 0,空结构体数组占用空间0
}
// 展示动态数组的内存地址,证明vec数组,数据存储在堆中
#[test]
fn show_vec_address() {
let x = 100;
let y = 1000;
let mut arr = vec![0, 1];
let z = 100;
// 内存合理
println!("show_vec_address
x {:p}", &x); // 0x4905afd2 68
println!("show_vec_address
y {:p}", &y); // 0x4905afd2 6c +4
println!("show_vec_address &arr
{:p}", &arr); // 0x4905afd2 70 +4
println!("show_vec_address &arr[0]{:p}", &arr[0]); // 0x1a2814 aac90 堆地址1
println!("show_vec_address
&z
{:p}", &z); // 0x4905afd2 8c +28
arr.push(10);
arr.push(10);
arr.push(10);
arr.push(10);
arr.push(10);
arr.push(10);
println!("show_vec_address
&arr
{:p}", &arr); // 0x4905afd2 70 不变
println!("show_vec_address &arr[0]{:p}", &arr[0]); // 0x1a2814 bbbc0 堆地址2
}
// 128 / 8 = 16
// 16 * 6 = 96
#[derive(Debug)]
pub struct SBigData {
val1: u128,
val2: u128,
val3: u128,
val4: u128,
val5: u128,
val6: u128,
}
impl Drop for SBigData {
fn drop(&mut self) {
println!("drop SBigData")
}
}
// 实现了Drop,结构体实例也只是存储在栈上
#[test]
fn show_big_struct_address() {
let x = 100;
let big = SBigData {
val1: 1,
val2: 2,
val3: 3,
val4: 4,
val5: 5,
val6: 5,
};
let y = 1000;
// 内存合理
println!(" x
{:p}", &x); // 0x8f775d 6e4
println!("{:p}", &big.val1); // 0x8f775d 6e8 +4
println!("{:p}", &big.val5); // 0x8f775d 728 +64 = 16*4
println!(" y
{:p}", &y); // 0x8f775d 74c +36 = 16*2 + 4
println!("{}", get_val_size(&big)); // 96
}
// 二维数组和C语言内存分配一致
#[test]
fn array_2d_usecase() {
let arr2d = [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]];
assert_eq!(get_var_type(arr2d), "[[i32; 3]; 4]"); // 行、列值编译时可明确,同样不支持VLA
assert_eq!(arr2d[2][0], 2);
assert_eq!(arr2d[2][1], 3);
assert_eq!(arr2d[2][2], 4);
// 内存合理
println!("
{:p}", &arr2d); // 0xdd94efd73 0
println!("{:p}", &arr2d[0][0]); // 0xdd94efd73 0
println!("{:p}", &arr2d[0][1]); // 0xdd94efd73 4 +4
println!("{:p}", &arr2d[0][2]); // 0xdd94efd73 8 +4
println!("{:p}", &arr2d[1][0]); // 0xdd94efd73 c +4
}
option_result.rs
use super::common::*;
// 使Rust语言提供null值的一种方案,Option枚举定义
// pub enum Option<T> {
//
None,
//
Some(T),
// }
// Option占用的内存空间
#[test]
fn generic_enum_test1() {
let e1: Option<()> = None;
assert_eq!(get_val_size(&e1), 1); // 1 + 0 = 1
let e2: Option<i8> = None;
assert_eq!(get_val_size(&e2), 2); // 1 + 1 = 2
let e3: Option<i16> = None;
assert_eq!(get_val_size(&e3), 4); // 1->2 + 2 = 4 其中1->被字节对齐了
let e4: Option<i32> = None;
assert_eq!(get_val_size(&e4), 8); // 1->4 + 4 = 8 其中1->被字节对齐了
let e5: Option<i64> = None;
assert_eq!(get_val_size(&e5), 16); // 1->8 + 8 = 16 其中1->被字节对齐了
let e6: Option<i128> = None;
assert_eq!(get_val_size(&e6), 24); // 1->8 + 16 = 24 其中1->被字节对齐了
}
#[test]
fn use_option_with_ownership_and_use_match() {
let some_string = Some(String::from("zhang xiao san")); // 带所有权
let some_string2 = some_string; // 发生了Move
// let val = match some_string {
//
Some(name) => { // name编译报错: value used here after move
//
println!("has value");
//
1
//
}
//
None => {
//
println!("none");
//
0
//
}
// };
let val = match some_string2 {
// Move
Some(name) => {
println!("has value: {}", name);
1
}
None => {
println!("none");
0
}
};
assert_eq!(val, 1);
// let val = match some_string2 {
//
Some(name) => { // name编译报错,value used here after move
//
println!("has value: {}", name);
//
1
//
}
//
None => {
//
println!("none");
//
0
//
}
// };
// assert_eq!(val, 1);
}
// 展示Option用于返回值及match用法
#[test]
fn use_option_for_null_return_or_not() {
fn get1(num: i32) -> Option<bool> {
if num > 10 {
return Some(true);
}
return None;
}
let result = get1(11);
// 即便返回值是bool,也可以有三个形态,None,true,false,None代表没有返回值
match result {
Some(val) => {
if val {
println!("true");
} else {
println!("false");
}
}
None => {
println!("None");
}
} // 这里不加“;”,也不会结束本函数
// assert_eq!(1,2); // 通不过测试,说明上面不加;也会执行到这里
struct _Person {
_name: String,
_man: bool,
}
fn get2(num: i32) -> Option<_Person> {
if num > 10 {
let me = _Person {
_name: String::from("zhang xiao san"),
_man: false,
};
return Some(me);
}
return None;
}
let result = get2(11);
// 判断null与非null
match result {
Some(val) => {
assert_eq!(val._name, "zhang xiao san");
}
None => {
println!("no one");
}
}
()
}
#[test]
fn use_option_base() {
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None; // 单None确定不了泛型,必须显式给出类型
assert_eq!(get_var_type(some_number), "core::option::Option<i32>");
assert_eq!(get_var_type(some_string), "core::option::Option<&str>");
assert_eq!(get_var_type(absent_number), "core::option::Option<i32>");
let mut x = Some(5);
let y = x.take(); //
take夺取了x内部的值
assert_eq!(x, None);
assert_eq!(y, Some(5));
let z = x.take();
assert_eq!(x, None);
assert_eq!(z, None); // 值已经被y夺取了,只夺取到了None
let x = Some(2);
let y = None;
assert_eq!(x.or(y), Some(2)); // Some(2).or(None) == Some(2);
let x = None;
let y = Some(100);
assert_eq!(x.or(y), Some(100)); // None.or(Some(100)) == Some(100)
let x = Some(2);
let y = Some(100);
assert_eq!(x.or(y), Some(2)); // Some(x).or(Some(y)) == Some(x)
let x: Option<u32> = None;
let y = None;
assert_eq!(x.or(y), None); // None.or(None) == None
}
// Result枚举原型:
// enum Result<T, E> {
//
Ok(T),
//
Err(E),
// }
#[test]
fn recovery_from_err1() {
use std::fs::File;
use std::io::ErrorKind;
let f1 = File::open("hello.txt");
assert_eq!(
get_var_type(&f1),
"&core::result::Result<std::fs::File, std::io::error::Error>"
);
// 检查open返回值
let _f2 = match f1 {
// 如果open成功
Ok(file) => file,
// 如果open返回Err,则查看其类型
Err(open_err) => match open_err.kind() {
// 如果open err类型是not found,则create
ErrorKind::NotFound => match File::create("hello.txt") {
// 如果create成功,则返回
Ok(f3) => f3,
// create失败,则直接panic
Err(creat_err) => panic!("create file error: {:?}", creat_err),
},
// 如果open err,其类型不是not found,则直接panic
_ => panic!("open file err: {:?}", open_err),
},
};
}
// Result<T,E>枚举有一些常用的的语法糖似的方法:
// 与recovery_from_err1功能一致
#[test]
fn recovery_from_err2() {
use std::fs::File;
use std::io::ErrorKind;
// unwrap_or_else:如果返回值是Ok(ret),则直接返回ret
// unwrap_or_else:如果返回值是Err(e),则执行闭包内容
let _f = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Problem creating the file: {:?}", error); // 这一行实际上返回了 ! 类型
})
} else {
panic!("Problem opening the file: {:?}", error); // 这一行实际上返回了 ! 类型
}
});
}
#[test]
fn use_result_unwrap() {
use std::fs::File;
// unwrap:如果返回值是Err(e),则直接panic!
let _f = File::open("hello.txt").unwrap();
// expect:如果返回值是Err(e),则带着参数内容(error!!)直接panic!
let _f = File::open("hello.txt").expect("error!!");
}
// _read_file_text 1 2 3 4功能一致
#[test]
fn read_file_text() {
use std::fs::File;
use std::io::Read;
// 读取文本文件内容,写法1
fn _read_file_text1() -> Result<String, std::io::Error> {
// open 如果返回Err,类型则是<std::io::Error>
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e), // 这里的e类型就是<std::io::Error>
};
let mut s = String::new();
// 把f文件中的内容读取入s,作为utf8字符串
// read_to_string如果返回Err,类型则是<std::io::Error>
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
// 如果读取文件内容成功,则返回总函数(s)
Err(e) => Err(e), // 如果读取文件内容失败,则返回总函数(e)
}
}
// 读取文本文件内容,写法2,展示当返回值是Result<T,E>类型时,?符的语法糖
fn _read_file_text2() -> Result<String, std::io::Error> {
// ? 如果 open 返回值实际上是Err,则直接返回函数
// ? 如果 open 返回值实际上是Ok,则获取Ok()中的内容给f
let mut f = File::open("hello.txt")?;
let mut s = String::new();
// ? 同上
f.read_to_string(&mut s)?;
Ok(s)
}
// 读取文本文件内容,写法3,?符连用
fn _read_file_text3() -> Result<String, std::io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
// 标准库封装的写法
fn _read_file_text4() -> Result<String, std::io::Error> {
std::fs::read_to_string("hello.txt")
}
}
// 如果 ? 前返回的Error类型与函数返回值Result中的Error类型不一致
// 会隐式自动调用 From trait 的方法完成转换
// 如果Error类型间未实现 From trait ,则编译报错
#[test]
fn return_result() {
// 定义自己的错误类型
struct MyError {
_err: String,
}
// 为MyError实现From trait,使 std::io::Error 可以自动转换成MyError
impl From<std::io::Error> for MyError {
// 这个Self相当于MyError
fn from(t: std::io::Error) -> Self {
MyError {
_err: format!("{:?}", t),
}
}
}
// 隐式错误类型转换
fn _func() -> Result<i32, MyError> {
std::fs::File::open("hello.txt")?;
Ok(5)
}
// 定义自己的错误类型2
struct MyError2 {
_errno: i32,
}
// 为MyError实现From trait,使 MyError2 可以自动转换成MyError
impl From<MyError2> for MyError {
fn from(t: MyError2) -> MyError {
MyError {
_err: format!("{}", t._errno),
}
}
}
// 以下编译报错说明: 只能?能隐式From转换,直接返回不进行隐式转换
fn _func3() -> Result<i32, MyError> {
// 编译报错:expected enum `std::result::Result`, found struct `return_result::MyError2`
// MyError2 { _errno: 0 }
// 编译报错:expected struct `return_result::MyError`, found struct `return_result::MyError2`
// Err(MyError2 { _errno: 0 })
let _ = _func4()?; // 只能?能隐式From转换
Ok(0)
}
fn _func4() -> Result<String, MyError2> {
Err(MyError2 { _errno: 1 })
}
}
// ? 还能用于 Option
#[test]
fn return_result2() {
fn _func() {
// let f = File::open("hello.txt")?;
// 编译报错:
// the `?` operator can only be used in a function that returns `Result` or `Option`
// (or another type that implements `std::ops::Try`
// std::ops::Try原型:
pub trait Try {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
fn from_error(v: Self::Error) -> Self;
fn from_ok(v: Self::Ok) -> Self;
} // 等开发需要时,再具体学习吧
// Result<T, E>自身就实现了Try
}
}
// 实现一把Try
// 编译报错:use of unstable library feature 'try_trait'
// std::ops::Try 目前属于不稳定库特性
// #[test]
// fn return_result3() {
//
struct MyType {
//
num1: u32,
//
num2: u32,
//
}
//
impl std::ops::Try for MyType {
//
type Ok = u32;
//
type Error = String;
//
fn into_result(self) -> Result<Self::Ok, Self::Error> {}
//
fn from_error(v: Self::Error) -> Self {}
//
fn from_ok(v: Self::Ok) -> Self {}
//
}
// }
// 使用Default
#[test]
fn use_default_trait() {
// trait定义:
// trait Default: Sized {
//
fn default() -> Self;
// }
struct Point {
x: i32,
y: i32,
}
impl Default for Point {
fn default() -> Point {
Point { x: 1, y: 2 }
}
}
fn get_address(size: i32) -> Option<Point> {
if size == 0 {
return Some(Point { x: 10, y: 20 });
}
None
}
// 这里如果有值了,就直接返回,否则调用default,生成一个默认值
// 如果 Point 没有实现 Default,则编译报错:
// method not found in `std::option::Option<option_result::use_default_trait::Point>`
let result = get_address(1).unwrap_or_default();
assert_eq!(result.x, 1);
assert_eq!(result.y, 2);
let result = get_address(0).unwrap_or_default();
assert_eq!(result.x, 10);
assert_eq!(result.y, 20);
}
ownership.rs
use super::common::*;
// 所有权!
// rust的生命周期等复杂概念,都是基于所有权规则来的
#[test]
fn ownership_heap_auto_malloc_and_free() {
let mut name = String::from("zhang");
// 自动malloc"zhang"所占《堆》上内存 m1,注意:所有权与堆没有直接关系。普通结构体实例也遵循所有权规则,但是内存放在栈上。
// m1可能占用5字节,也可能更多,看String具体实现
// name自身是一个栈上的内部带指针字段的结构体变量,是m1的拥有者,即拥有m1所有权
// name自身自用栈上空间 ptr 8 + len 8 + cap 8 = 24字节或更少,看String的实现
// 拥有权规则1:m1必须有一个拥有者(owner),可以是name这个变量,也可以指定给其他变量。
// 拥有权规则2:m1在任意时刻,只有一个owner
// 拥有权规则3:当owner离开作用域时,m1被free。
name.push(' '); // 可能发生relloc
name.push_str("san"); // 可能发生relloc
println!("{}", name);
assert_eq!(name, "zhang san");
} // <-- 这里对name拥有的堆内存m1释放
// name离开作用域时,编译器自动调用其drop方法,进行释放m1
// name自身是个变量,存储在栈上,也会随函数调用结束释放。
// 所有权转移1
#[test]
fn ownership_move_usecase1() {
let x = 5;
let y = x;
println!("{} {}", x, y); // x和y之间并没有所有权概念和关系,因为他们都被存储在栈上,i32实现了Copy trait。
let n1 = String::from("zhang san"); // 参考 ownership_heap_auto_malloc_and_free
let n2 = n1; // 所有权由n1,让给了n2,这里发生了结构体潜拷贝,堆内存内容则没有变
// println!("{}", n1); 编译报错,因为n1已经废掉了,江湖上以后,再也没有n1了
println!("{}", n2);
} // 这里只free n2,因为所有权已转让给n2,n1已没了。
// 所有权转移2
#[test]
fn ownership_move_usecase2() {
let mut x = 5;
let y = &mut x;
// let z = &mut x; // 编译报错:cannot borrow `x` as mutable more than once at a time
*y += 1;
func(y);
// func不会拿走y的所有权
*y += 1;
fn func(pa: &mut i32) {
println!("{}", pa);
}
// get_var_type 拿走了y的所有权
assert_eq!(get_var_type(y), "&mut i32");
// *y += 1; // 编译报错:value used here after move
}
// 所有权转移3
#[test]
fn ownership_move_usecase3() {
let mut x = 5;
let y = &mut x;
let z = y;
// 编译报错:
// value used here after move
// move occurs because `y` has type `&mut i32`, which does not implement the `Copy` trait
// 说明&mut x类型,可变引用,也遵循所有权规则
// *y += 1;
*z += 1;
assert_eq!(x, 6);
}
// 调用函数ownership_move_function1时,通过入参和返回值,转移了所有权
#[test]
fn ownership_move_function_param_return() {
let name1 = String::from("zhang san");
// 调用函数后,name1已无效,其所有权已Move到函数入参里了。
// name2通过返回值,获取了函数出让的所有权。
let name2 = ownership_move_function1(name1);
println!("{}", name2);
} // free name2
pub fn ownership_move_function1(mut name: String) -> String {
// 从调用者处,移动其所有权给到参数name
println!("{}", name);
name.push('.'); // 拥有所有权,爱咋改咋改。
return name; // name所占用的内存不会被free,其所有权以函数返回值的方式,转移给了函数的调用者
}
// 结构体要么实现Drop trait,要么实现Copy trait,不可两者都实现
// 如下是一些 Copy 的类型:
//
所有整数类型,比如 u32。
//
布尔类型,bool,它的值是 true 和 false。
//
所有浮点数类型,比如 f64。
//
字符类型,char。
//
元组,当且仅当其包含的类型也都是 Copy 的时候。比如,(i32, i32) 是 Copy 的,但 (i32, String) 就不是。
//
注意:结构体和枚举都不是Copy的,都遵循所有权规则
// 实现了Copy trait的类型,一个旧的变量在将其赋值给其他变量后仍然可用,即:不遵循所有权转移规则
#[derive(Copy, Clone)] // 这个注解,就为CopyStruct实现了Copy trait
struct CopyStruct {
_x: i32,
_y: i32,
_z: i32,
_w: i32,
}
// trait原型:
// pub trait Sized {}
// Clone继承自Sized
// pub trait Clone: Sized {
//
fn clone(&self) -> Self;
// }
// Copy 继承自Clone
// pub trait Copy: Clone {}
// 所以:如果要实现Copy,则必须实现Clone
// 所以:CopyStruct的标注,单标注一个Copy不行
#[test]
fn copy_trait_has_no_ownership() {
let a = CopyStruct {
_x: 1,
_y: 2,
_z: 3,
_w: 4,
};
// 这里发生的是Copy,而不是Move,没有所有权,更没有所有权转移
// 类似于i32,后续a、b都还能继续使用
let b = a;
let c = b;
assert_eq!(a._x, b._x);
assert_eq!(a._y, b._y);
assert_eq!(c._z, b._z);
}
#[derive(Copy, Clone)]
struct CopyStruct2 {
_x: i32,
_y: i32,
_z: i32,
}
// 结构体栈上内存分布合理
#[test]
fn struct_mem_show() {
let a = CopyStruct {
_x: 1,
_y: 2,
_z: 3,
_w: 4,
};
let b = a;
let c = b;
// 内存合理
println!("{:p}
", &a); // 0x15eefdd830
println!("{:p}", &a._x); // 0x15eefdd830 +0
println!("{:p}", &a._y); // 0x15eefdd834 +4
println!("{:p}", &a._z); // 0x15eefdd838 +4
println!("{:p}", &a._w); // 0x15eefdd83c +4
println!("{:p}
", &b); // 0x15eefdd840 +4
println!("{:p}
", &c); // 0x15eefdd850 +16
println!("{}", get_val_size(&a)); // 16
let x = 0;
let d = CopyStruct2 {
_x: 1,
_y: 2,
_z: 3,
};
let y = 1;
// 内存合理
println!("{:p}
", &x); // 0xd4436fd9 2c
println!("{:p}
", &d); // 0xd4436fd9 30 +4
println!("{:p}
", &y); // 0xd4436fd9 3c +12
println!("{}", get_val_size(&d)); // 12
}
// 所有权借用:不可变引用
#[test]
fn ownership_immutable_borrow() {
let name = String::from("zhang san");
ownership_immutable_borrow_function(&name);
} // name在此处释放
pub fn ownership_immutable_borrow_function(name: &String) {
println!("just print: {}", name); // 没有所有权,不可变,只能读。
} // 没有所有权,不会释放name
// 所有权借用:可变引用
#[test]
fn ownership_mutable_borrow() {
let mut name = String::from("zhang san");
ownership_mutable_borrow_function(&mut name);
} // name在此处释放
pub fn ownership_mutable_borrow_function(name: &mut String) {
name.push_str("? hehe!"); // 没有所有权,可修改,可读。
println!("fix and print: {}", name);
} // 没有所有权,不会释放name
// 所有权借用中,引用,就是C语言的指针
// 所有权借用中,引用的可变与不可变,是rust编译器来检查区分的,运行时都是指针,本质上无区别
// 所有权借用规则1:在《特定作用域》中的特定数据最多只能有一个可变引用。此规则目的保证:不存在两个或更多指针同时写同一数据
// 所有权借用规则2:在《特定作用域》中要么《只能》有一个可变引用,要么《只能》有多个不可变引用
//
对数组、String等本体修改行为会产生另一个可变引用
// 所有权借用规则3:引用必须总是有效的,不能有本体都释放了,借用行为(引用)还在的情况。rust编译器会保证这一点。
// 即便是i32类型也满足以上借用规则
#[test]
fn ownership_mutable_borrow_of_copy_type() {
let mut x = 0;
let xp1 = &mut x;
// let xp2 = &mut x; // 编译报错: cannot borrow `x` as mutable more than once at a time
*xp1 = 1; // 本质上就是C语言的指针
assert_eq!(x, 1);
}
pattern_mode.rs
use super::common::*;
// 模式匹配:强大的表达力
// 模式匹配,解构元组
#[test]
fn pattern_matching_destructure_tuple() {
let tup = (500, 6.4f32, 1);
let (n, x, _) = tup; // 把元组解构给变量,包括值和类型,_为忽略对应位置的字段
assert_eq!(n, 500);
assert_eq!(get_var_type(n), "i32");
assert_eq!(x, 6.4);
assert_eq!(get_var_type(x), "f32");
// 通过.0 .1 .2所引方式获取元组内数据元素
assert_eq!(n, tup.0);
assert_eq!(x, tup.1);
assert_eq!(get_var_type(n), get_var_type(tup.0));
assert_eq!(get_var_type(x), get_var_type(tup.1));
}
// if | if let | else | else if | else if let 混用
#[test]
fn use_if_else_iflet_else() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
if let Some(color) = favorite_color {
println!("Using your favorite color, {}, as the background", color);
} else if is_tuesday {
println!("Tuesday is green day!");
} else if let Ok(age) = age {
if age > 30 {
println!("Using purple as the background color");
} else {
println!("Using orange as the background color");
}
} else {
println!("Using blue as the background color");
}
}
// match1 匹配给特殊值
#[test]
fn use_match_1() {
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => (),
}
}
// match2 匹配值给x
#[test]
fn use_match_2() {
let some_u8_value = Some(0u8);
match some_u8_value {
Some(x) => {
println!("{}", x);
assert_eq!(x, 0);
}
_ => (),
}
}
// if let 相当于 match1
// 使用 if let 意味着编写更少代码,更少的缩进和更少的样板代码
// 然而,这样会失去 match 强制要求的穷尽性检查
// match 和 if let 之间的选择依赖特定的环境以及增加简洁度和失去穷尽性检查的权衡取舍
#[test]
fn use_if_let_eq_match1() {
let some_u8_value = Some(3u8);
if let Some(3) = some_u8_value {
println!("three");
} else {
// 这里相当于 match 的 _
}
}
// if let 相当于 match2
#[test]
fn use_if_let_eq_match2() {
let some_u8_value = Some(3u8);
if let Some(x) = some_u8_value {
println!("{}", x);
assert_eq!(x, 3);
}
// else None
}
// while let: 和while用法一样
#[test]
fn use_while_let() {
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
// 当匹配不上的时候,退出
while let Some(top) = stack.pop() {
println!("{}", top); // 打印出:3 2 1
}
}
// 使用for来解构元组
#[test]
fn use_for_match() {
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
// 打印效果:
// a is at index 0
// b is at index 1
// c is at index 2
println!("{} is at index {}", value, index);
}
}
// 函数参数解构
#[test]
fn use_function_param_match() {
// 入参只有一个,要求类型为: &(i32, i32)
// 进来后,直接解构成 x 和 y
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("Current location: ({}, {})", x, y);
}
let point = (3, 5);
print_coordinates(&point);
// 入参匹配,相当于let匹配
let &(_x, _y) = &point;
assert_eq!(get_var_type(_x), "i32");
assert_eq!(get_var_type(_y), "i32");
assert_eq!(_x, 3);
assert_eq!(_y, 5);
}
// Refutability(可反驳性): 模式是否会匹配失效
// 必定匹配成功的:
// let x = 5;
// for
// 入参
// match(最后一个分支 _)
// 可能匹配失败的:
// if let
// while let
// match(非_分支)
// match的各分支是从上到下一个个匹配的,满足后,就不再进行下面的匹配了
#[test]
fn use_match1() {
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got x = 50"),
Some(val) => println!("Matched, val = {:?}", val),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end: x = {:?}, y = {:?}", x, y);
}
#[test]
fn use_match2() {
let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
}
#[test]
fn use_match3() {
let x = 5;
match x {
1..=5 => println!("one through five"),
_ => println!("something else"),
}
// 相当于:
match x {
1 | 2 | 3 | 4 | 5 => println!("one through five"),
_ => println!("something else"),
}
}
#[test]
fn use_match4() {
let x = 'c';
match x {
'a'..='j' => println!("early ASCII letter"),
'k'..='z' => println!("late ASCII letter"),
_ => println!("something else"),
}
}
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
#[test]
fn use_match5() {
let p = Point { x: 0, y: 7 };
match p {
// 匹配y轴上坐标点
Point { x, y: 0 } => println!("On the x axis at {}", x),
// 匹配x轴上坐标点
Point { x: 0, y } => println!("On the y axis at {}", y),
// 匹配其他的坐标点
Point { x, y } => println!("On neither axis: ({}, {})", x, y),
}
}
#[test]
fn use_match6() {
let num = Some(4);
match num {
Some(x) if x < 5 => println!("less than five: {}", x),
Some(x) => println!("{}", x),
None => (),
}
}
#[test]
fn use_match7() {
let x = 4;
let y = false;
let result;
match x {
// 当x == 4 || 5 || 6
并且 满足 y == true
4 | 5 | 6 if y => {
println!("yes");
result = 0;
}
_ => {
println!("no");
result = 1;
} // 实际会走到这里
}
assert_eq!(result, 1);
}
#[test]
fn use_match8() {
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id: val @ 3..=7, // 如果 id 的值为 3 4 5 6 7,则走此分支,顺便把id的值绑到 val 上
} => {
println!("val: {}", val);
}
Message::Hello { id: 10..=12 } => {
println!("id: {}", 0); // 这里id不是一个变量,不能用
}
Message::Hello { id } => {
println!("Found some other id: {}", id) // 这里的id是一个变量,可以用
}
}
}
#[test]
fn use_let_match1() {
let p = Point { x: 0, y: 7 };
// 匹配1
let Point { x: a, y: b } = p; // 不会夺取所有权
assert_eq!(0, a);
assert_eq!(7, b);
// 匹配2
let Point { x, y } = p;
assert_eq!(0, x);
assert_eq!(7, y);
// 内存就像四个i32一样分配
println!("{:p}", &p); // 0x3e232fd6 68
println!("{:p}", &a); // 0x3e232fd6 70 +8
println!("{:p}", &b); // 0x3e232fd6 74 +4
println!("{:p}", &x); // 0x3e232fd7 78 +4
println!("{:p}", &y); // 0x3e232fd7 7c +4
}
#[test]
fn use_let_match2() {
let ((feet, inches), Point { x, y }) = ((3.0, "hehe"), Point { x: 3, y: -10 });
assert_eq!(feet, 3.0);
assert_eq!(inches, "hehe");
assert_eq!(x, 3);
assert_eq!(y, -10);
}
#[test]
fn use_let_match3() {
let numbers = (2, 4, 8, 16, 32);
let (first, _, third, _, fifth) = numbers;
println!("Some numbers: {}, {}, {}", first, third, fifth);
match numbers {
(first, _, third, _, fifth) => {
println!("Some numbers: {}, {}, {}", first, third, fifth)
}
}
}
#[test]
fn use_let_match4() {
let name = String::from("zhang san");
let _ = name; // No Move
let _name2 = name; // Move
// let _name3 = name; // 编译报错
}
#[test]
fn use_let_match5() {
let (x, ..) = (1, 2, 3, 4, 5);
assert_eq!(x, 1);
let (.., x) = (1, 2, 3, 4, 5);
assert_eq!(x, 5);
let (x, .., y) = (1, 2, 3, 4, 5);
assert_eq!(x, 1);
assert_eq!(y, 5);
}
process.rs
// cargo: 语言自带编译和包管理工具,无需make、CMake等第三方构建、编译工具
// 常用cargo命令:
// cargo --version
// cargo new project-name
// cargo new --lib project-name
// cargo build
// cargo build --release
// cargo run 编译并运行
// cargo check 只检查编译
// cargo test 生成的二进制文件的默认行为是并行的运行所有测试,并截获测试运行过程中产生的输出,阻止他们被显示出来,使得阅读测试结果相关的内容变得更容易。
// cargo test -p mylib
// cargo test -- --nocapture (不截获标准输出内容)
// cargo test -- --nocapture --test-threads=1 (非并行执行,不截获)
// cargo test xxx -- xxx 需要先列出传递给 cargo test 的参数,接着是分隔符 --,再之后是传递给测试二进制文件的参数。
// cargo test use_global_constant 只运行use_global_constant测试函数
// cargo test use 运行所有以use开头的测试用例
// cargo test -- --ignored 单独执行所有带[ignored]的测试用例
// cargo test --test integration_test : --test后跟文件的名称来运行某个特定集成测试文件中的所有测试
// cargo doc 生成文档注释的 HTML 文档,放在target/doc目录
// cargo doc -p lib1 lib为包名
// cargo doc -p mylib --open 为mylib包生成网页文档后,直接用浏览器打开
// rustdoc --test main.rs
// panic!必定使进程崩溃,无法恢复,无法拦截。
// panic!的日志:
// 默认情况:
// ./hello_rust.exe
// thread 'main' panicked at '!!!', srcmain.rs:305:5
// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
// 带参数:
// RUST_BACKTRACE=1 ./hello_rust.exe
// thread 'main' panicked at '!!!', srcmain.rs:305:5
// stack backtrace:
//
0: std::panicking::begin_panic<str*>
//
at C:Users86152.rustuptoolchainsstable-x86_64-pc-windows-msvclibrustlibsrcrustlibrarystdsrcpanicking.rs:521
//
1: hello_rust::add_function1
//
at D:linuxLndrust_study_roothello_rustsrcmain.rs:305
//
2: hello_rust::clear_compile_alarm
//
at D:linuxLndrust_study_roothello_rustsrcmain.rs:47
//
3: hello_rust::main
//
at D:linuxLndrust_study_roothello_rustsrcmain.rs:23
//
4: core::ops::function::FnOnce::call_once<fn(),tuple<>>
//
at C:Users86152.rustuptoolchainsstable-x86_64-pc-windows-msvclibrustlibsrcrustlibrarycoresrcopsfunction.rs:227
// note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace
remark.rs
//! hello_rust 是一个rust语言特性练习二进制crate;
//!
//! 这种注释(带一个叹号)会出现在html文档的最外层来描述这个crate;
//!
//! 这通常用于 crate 根文件(通常是 src/lib.rs)或模块的根文件为 crate 或模块整体提供文档
/// Adds one to the number given.
///
/// # Examples
/// ```
/// let arg = 5;
/// let answer = _add_one(arg);
/// assert_eq!(6, answer);
/// ```
pub fn _add_one(x: i32) -> i32 {
x + 1
}
// 文档注释:三道杠
// 带#的都会在文档中生成一个标题,除了Examples,还可以有:Panics Errors Safety
smart_pointer.rs
use super::common::*;
// 使用Box<T>,创建堆上的数据
#[test]
fn use_box_keep_data_at_heap() {
let x = 100;
let mut y = Box::new(20i32);
let z = 100;
println!("{:p}
", &x); // 0x1a6b6fd7 7c
println!("{:p}
", &y); // 0x1a6b6fd7 80 +4
println!("{:p}
", &z); // 0x1a6b6fd7 8c +12 猜测 ptr + len
println!("{:p}", y.as_ref()); // 0x144fc6c93d0 堆上内存
assert_eq!(get_var_type(&y), "&alloc::boxed::Box<i32>");
assert_eq!(get_var_type(y.as_ref()), "&i32");
assert_eq!(get_var_type(y.as_mut()), "&mut i32");
} // <-- 这个位置,free y所占用的堆内存
// 啥时候用Box:
// 编译时未知大小,又想要在需要确切大小的上下文中使用这个类型值的时候,比如trait对象数组
// 大量数据,不拷贝的情况下,转移所有权
// 希望拥有一个值并只关心它的类型是否实现了特定 trait 而不是其具体类型,比如trait对象数组
// 使用Box制作链表
// 原理和原因与C语言一毛一样
// C语言定义结构体内部,不能包含自身实例,因为这样会无限递归。但是可以包含自身指针。
// box 只提供了间接存储和堆分配;他们并没有任何其他特殊的功能
// Box<T>变量的本质就像malloc后,返回的指针变量
#[test]
fn use_box_make_linked_list() {
// 编译报错:
// 递归类型,拥有无限大的size
// recursive type `use_box_make_linked_list::LinkedNode` has infinite size
// enum LinkedNode {
//
Data(i32, LinkedNode),
//
Nil,
// }
// 允许这么定义,因为Box<LinkedNode>的大小是固定的
enum LinkedNode {
Data(i32, Box<LinkedNode>),
Nil,
}
// 初始化一个单链表,一共有三个节点,值分别是0、1、2,最后一个2节点,指向一个Box<LinkedNode::Nil>
let first_node = LinkedNode::Data(
0,
Box::new(LinkedNode::Data(
1,
Box::new(LinkedNode::Data(2, Box::new(LinkedNode::Nil))),
)),
);
// 一个节点的长度为16??
assert_eq!(get_val_size(&first_node), 16);
// 详细解一下first_node所占的空间大小
if let LinkedNode::Data(x, y) = first_node {
assert_eq!(get_val_size(&x), 4);
assert_eq!(get_val_size(&y), 8);
} else {
panic!("!!");
}
// 猜测一共16 = 8 + 4(1) + 4(2) 其中8是Box的,4(1)是i32的,4(2)是枚举类型的
// 编译报错:y
use of moved value,因为上面if let的时候,已经把Box的所有权拿走了
// if let LinkedNode::Data(x, y) = first_node {
// }
}
// 解引用运算符
// 没有实现 Deref trait 的话,编译器只会 * 解引用真正的 & 引用类型。
#[test]
fn use_deref_trait0() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y); // 解引用运算符,本质上同C语言解引用
}
// 我的Box元组结构体,和标准库的Box<T>不同,我的数据存储在栈上
struct MyBox<T>(T);
// 实现Deref trait
impl<T> std::ops::Deref for MyBox<T> {
type Target = T;
// 返回值类型,写法上,&Self::Target 和 &T 可以互换
fn deref(&self) -> &Self::Target {
&self.0 // 返回元组结构体第一个字段的引用
}
}
// 直接 * 解引用
#[test]
fn use_deref_trait1() {
let y = MyBox(5);
// assert_eq!(5, *y);
// 如果MyBox不实现 Deref trait,则上面一行编译报错:本类型不能被解引用
// type `use_deref_trait1::MyBox<{integer}>` cannot be dereferenced
assert_eq!(5, *y);
// MyBox实现了Deref trait后,现在 * 解引用不再报错了
let z = *y; // *y 解引用后是 i32 类型
assert_eq!(get_var_type(z), "i32");
assert_eq!(5, z);
// *y Rust 事实上在底层运行了如下代码:*(y.deref())
// y 初始类型:MyBox<i32>
// *y =>
// *(MyBox<i32>.deref()) =>
// *((&MyBox<i32>).deref()) =>
// *(&i32) =>
// i32
// 注意:这里 y 的类型是 MyBox<i32>,而不是引用
// 调用deref,这里会自动转换成 (&y).deref() 即:自动完成不可变引用的转换
// 每次当我们在代码中使用 * 时, * 运算符都被替换成了先调用 deref 方法再接着使用 * 解引用的操作
// 调用 deref 方法可以发生多次
// 但是,* 运算只会发生一次
}
// 传参和直接 *解引用的自动转换还不太一样
#[test]
fn use_deref_trait2() {
// 这里参数类型要求:&i32
fn say_hello(num: &i32) {
println!("hello {}", num);
}
let y = MyBox(5);
// 编译报错:expected `&i32`, found struct `use_deref_trait1::MyBox`
// say_hello(y);
// 编译ok,从 &MyBox<i32> 自动解成了 &i32,直接就是Deref trait的 deref 方法
say_hello(&y);
// 编译报错:expected `&i32`, found `i32`
// say_hello(*y);
say_hello(&(*y));
}
// String类型的解引用
#[test]
fn use_deref_trait3() {
fn say_hello2(name: &str) {
println!("hello {}", name);
}
let name = MyBox(String::from("zhang san"));
// 自动多层解引用: &MyBox<String> -> &String -> &str
// 解引用强制多态将自动发生。这时会有一系列的 deref 方法被调用,把我们提供的类型转换成了参数所需的类型
// 当所涉及到的类型定义了 Deref trait,Rust 会分析这些类型并使用任意多次 Deref::deref 调用以获得匹配参数的类型
// 这些解析都发生在编译时,所以利用解引用强制多态并没有运行时惩罚!
say_hello2(&name);
// &name 相当于 &(*name)[..]
say_hello2(&(*name)[..]);
// 即:
let name2 = &(*name)[..];
assert_eq!(get_var_type(name2), "&str");
// 转换过程:
// *name => String 成了 &String[..]; 即标准的生成切片的语法
// &String[..] => &str 取其全量切片
}
// 为什么传参时,《&String》能作为《&str》用
// 因为:解引用强制多态(deref coercions)
// 将实现了 Deref 的《类型A的引用》 转换为 类型A通过 Deref 《所能够转换的》类型的引用
#[test]
fn use_deref_trait4() {
// 标准库实现:
// impl Deref for String {
//
type Target = str;
//
fn deref(&self) -> &str { // 注意这里的入参是&self,返回类型是&str
//
unsafe { str::from_utf8_unchecked(&self.vec) }
//
}
// }
let my_name = String::from("zhang san");
// 为什么 *my_name 类型是str,而不直接是&str
// 因为这个《解引用强制多态》是自动且连续的,一层一层解到了str
// 先解一层成为 &str,再自动解一层成为 str
// 所以必须要&一下,才能得到&str
let name_qstr = &(*my_name);
assert_eq!(get_var_type(name_qstr), "&str");
// 注意这里不能直接传my_name,只能传其引用,才能完成自动转换
// 直接传my_name,就和传递所有权混在一起了
// 这里的&String 转换成 &str后,就不再自动进行下一层解引用成 str 了,因为已经符合参数类型要求了
debug_it(&my_name);
fn debug_it(name: &str) {
println!("{}", name);
}
// 经历了 * 和 传参数解引用强制多态,仍旧保留有String的所有权
println!("{}", my_name);
}
// Box<T>实现了Deref trait
// 实际上有两种解引用trait:
// Deref trait 重载不可变引用的 * 运算符
// DerefMut trait 用于重载可变引用的 * 运算符
// 当 T: Deref<Target=U> 时从 &T 到 &U。
// 当 T: DerefMut<Target=U> 时从 &mut T 到 &mut U。
// 当 T: Deref<Target=U> 时从 &mut T 到 &U。
#[test]
fn use_deref_trait5() {
let mut my_name = String::from("zhang san");
let my_name = &mut my_name;
debug_it(my_name); // 从 &mut T 到 &U
fn debug_it(name: &str) {
println!("{}", name);
}
debug_it_mut(my_name); // 从 从 &mut T 到 &mut U。
fn debug_it_mut(name: &mut str) {
println!("{}", name);
}
}
// Drop trait定义:
// pub trait Drop {
//
fn drop(&mut self);
// }
struct MyData {
name: String,
}
impl Drop for MyData {
fn drop(&mut self) {
println!("MyData is droped! {}", self.name);
}
}
// 被动drop
#[test]
fn show_drop_trait1() {
let d1 = MyData {
name: String::from("zhang san"),
};
let _d2 = d1;
// 会先drop _d2.name
// 这里会打印出《一份》 MyData is droped! zhang san
}
// 被动drop,自动多级嵌套drop
// 推测更多级嵌套,仍旧能够完成drop
#[test]
fn show_drop_trait2() {
struct MyBox {
_data: MyData,
}
let _d1 = MyBox {
_data: MyData {
name: String::from("zhang san xx"),
},
};
// 嵌套的变量,_d1._data自动执行drop
// 这里会打印出一份 MyData is droped! zhang san xx
}
// 主动drop
#[test]
fn show_drop_trait3() {
let d1 = MyData {
name: String::from("li si"),
};
drop(d1); // 这里会调用d1的drop方法,打印出:MyData is droped! li si
// let _d2 = d1; // d1编译报错:value used here after move
println!("yes!"); // yes!
let _d2 = MyData {
name: String::from("wangwu"),
};
// 这里会打印出一份 MyData is droped! wangwu
}
// Rc<T> 引用计数智能指针
// 引用计数(reference counting)
// Box<T> 相当于一对一的软连接
// Rc<T> 相当于多对一的硬链接,并且实际数据处,存储了硬链接的数量
// Rc<T> 只提供不可变引用
// 不可在多线程场景下使用
#[test]
fn show_rc_case1() {
use std::rc::Rc;
// x1存储在栈上,就是一个指针
// 存储在堆中:占用堆内存总长度、硬链接计数、实际数据(此处为i32型)
let x1 = Rc::new(5);
assert_eq!(get_val_size(&x1), 8);
}
// 硬引用计数
#[test]
fn show_rc_case2() {
use std::rc::Rc;
let x1 = Rc::new(5);
assert_eq!(Rc::strong_count(&x1), 1);
// clone常用写法
let x2 = Rc::clone(&x1); // 栈上复制处一个x2, 堆里数据不变,硬链接计数++
assert_eq!(Rc::strong_count(&x1), 2);
assert_eq!(Rc::strong_count(&x2), 2);
// clone不常用写法
let x3 = x2.clone();
assert_eq!(Rc::strong_count(&x1), 3);
assert_eq!(Rc::strong_count(&x2), 3);
assert_eq!(Rc::strong_count(&x3), 3);
// 指向的数据地址一致
assert_eq!(x1.as_ref(), x2.as_ref());
assert_eq!(x1.as_ref(), x3.as_ref());
// 此处获取了数据指针,而不能让用户获取硬链接计数和堆内存长度
assert_eq!(get_var_type(x1.as_ref()), "&i32");
drop(x1);
assert_eq!(Rc::strong_count(&x2), 2);
assert_eq!(Rc::strong_count(&x3), 2);
drop(x2);
assert_eq!(Rc::strong_count(&x3), 1);
} // <-- 这里x3也被drop,此时发现硬链接计数 == 0,会完成堆内存的清理
// 遵循内部可变性模式的 RefCell<T> 类型
// 内部实现使用了 unsafe 代码
// 不同于 Rc<T>,RefCell<T> 代表其数据的唯一的所有权
// RefCell<T> 运行时检查所有权规则,编译时,不会检查
// RefCell<T> 正是用于当你确信代码遵守借用规则,而编译器不能理解和确定的时候。
// 三者区别:
// Box<T>
有单一所有者
// Rc<T>
允许相同数据 有多个所有者
// RefCell<T>
有单一所有者
// Box<T>
允许在 编译时 执行不可变或可变借用检查
// Rc<T>
仅允许在 编译时 执行不可变借用检查
// RefCell<T>
允许在 运行时 执行不可变或可变借用检查
// RefCell<T>.borrow()
= Ref<>
// RefCell<T>.borrow_mut() = RefMut<>
// 我们可以在即便 RefCell<T> 自身是不可变的情况下修改其内部的值
#[test]
fn try_use_refcell_t1() {
// 这是一个外部crate的trait定义
// 其定义,在我们的包里,没法改变
trait Messenger {
fn send(&self, msg: &str); // 注意这里的定义方式&self,不可变引用
}
struct Email {} // 发邮件
impl Messenger for Email {
fn send(&self, msg: &str) {
println!("Email ok, content: {}", msg);
}
}
struct QQMsg {} // 发QQ消息
impl Messenger for QQMsg {
fn send(&self, msg: &str) {
println!("QQMsg ok, content: {}", msg);
}
}
struct MicroBlog {} // 发微博
impl Messenger for MicroBlog {
fn send(&self, msg: &str) {
println!("MicroBlog ok, content: {}", msg);
}
}
struct BlackBox1 {
_cached_msgs: Vec<String>,
}
impl Messenger for BlackBox1 {
fn send(&self, msg: &str) {
// self._cached_msgs.push(msg.to_string());
// self._cached_msgs 编译报错
// cannot borrow `self._cached_msgs` as mutable, as it is behind a `&` reference
// `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
println!("BlackBox1 ok, content: {}", msg);
}
}
use std::cell::RefCell;
struct BlackBox2 {
cached_msgs: RefCell<Vec<String>>, // 这里用RefCell包裹起来
}
impl Messenger for BlackBox2 {
fn send(&self, msg: &str) {
self.cached_msgs.borrow_mut().push(msg.to_string()); // borrow_mut
println!("BlackBox1 ok, content: {}", msg);
}
}
let my_log_box = BlackBox2 {
cached_msgs: RefCell::new(Vec::new()),
};
my_log_box.send("hello");
my_log_box.send("world");
my_log_box.send("heheda");
assert_eq!(my_log_box.cached_msgs.borrow()[0], "hello");
assert_eq!(
my_log_box.cached_msgs.borrow().get(2),
Some(&String::from("heheda"))
);
// 如果胆敢违背规则如下:
// let box1 = my_log_box.cached_msgs.borrow_mut();
// let box2 = my_log_box.cached_msgs.borrow_mut();
// box2这一行直接报运行时panic
// thread 'main' panicked at 'already borrowed: BorrowMutError', srcmain.rs:3270:39
assert_eq!(
get_var_type(my_log_box.cached_msgs.borrow()),
"core::cell::Ref<alloc::vec::Vec<alloc::string::String>>"
);
assert_eq!(
get_var_type(my_log_box.cached_msgs.borrow_mut()),
"core::cell::RefMut<alloc::vec::Vec<alloc::string::String>>"
);
}
// RefCell<T>原理:就像编译时借用规则一样
// RefCell<T> 在任何时候只允许有多个不可变借用《或》一个可变借用
// RefCell<T> 记录当前有多少个活动的 Ref<T> 和 RefMut<T> 智能指针
// 每次调用 borrow,RefCell<T> 将活动的不可变借用计数加一,当 Ref<T> 值离开作用域时,不可变借用计数减一
// 同理,堆RefMut一样操作
// RefCell<T> 的一个常见用法是与 Rc<T> 结合
#[test]
fn use_rc_and_refcell() {
use std::cell::RefCell;
use std::rc::Rc;
// 一共就一份数据
let cell = RefCell::new(5);
// 产生两个引用
let rc1 = Rc::new(cell);
let rc2 = Rc::clone(&rc1);
// cell已经被用掉了
// borrow of moved value: `cell`
// println!("{:?}", cell);
// 修改其中一个
*(rc1.as_ref().borrow_mut()) = 10;
// 另一个Rc包裹同样的值也被修改了
assert_eq!(*(rc2.as_ref().borrow_mut()), 10);
}
// Cell<T> 类似 RefCell<T>
// 区别: Cell<T>不提供内部值的引用,而是把值拷贝进和拷贝出 Cell<T>
// 制造循环引用,进而导致内存泄漏
// 很难制造这个场景
// 必须是 Rc<RefCell<Rc<T>>> 这种类型
#[test]
fn make_mem_leak1() {
use std::cell::RefCell;
use std::rc::Rc;
// 单链表
#[derive(Debug)]
enum Node {
Data(i32, RefCell<Rc<Node>>),
Nil,
}
use Node::*;
// a、b都是只有一个数据节点,a -> b -> Nil
let node_b = Rc::new(Data(0, RefCell::new(Rc::new(Nil))));
let node_a = Rc::new(Data(1, RefCell::new(Rc::clone(&node_b))));
assert_eq!(
get_var_type(node_b.as_ref()),
"&hello_rust::smart_pointer::make_mem_leak1::Node"
);
// 蛇咬自己尾巴 a -> b -> a
if let Data(_num, next) = node_b.as_ref() {
assert_eq!(
get_var_type(next),
"&core::cell::RefCell<alloc::rc::Rc<hello_rust::smart_pointer::make_mem_leak1::Node>>"
);
*(next.borrow_mut()) = Rc::clone(&node_a);
}
assert_eq!(Rc::strong_count(&node_b), 2); // 2个强引用
assert_eq!(Rc::strong_count(&node_a), 2); // 2个强引用
// println!("{:?}", node_b);
// 上面这行打印,会导致栈溢出
// 打印效果:无限循环
// Data(0, RefCell { value: Data(1, RefCell { value: Data(0, RefCell { value: Data(1, RefCell { value: Data(0, RefCell
drop(node_a);
assert_eq!(Rc::strong_count(&node_b), 2); // 主动释放掉a, b的强引用还是2
} // <-- 这里会造成内存泄漏,不能正确drop掉a和b节点
// 使用弱引用(weak reference)
// 定义树类型结构体节点
#[test]
fn show_weak_ref() {
use std::cell::RefCell;
use std::rc::Rc;
use std::rc::Weak;
#[derive(Debug)]
struct Node {
value: i32,
parent: RefCell<Weak<Node>>,
children: RefCell<Vec<Rc<Node>>>,
}
let root = Rc::new(Node {
value: 0,
parent: RefCell::new(Weak::new()), // 最早的时候,无父级
children: RefCell::new(vec![]),
});
let child1 = Rc::new(Node {
value: 0,
parent: RefCell::new(Weak::new()), // 最早的时候,无父级
children: RefCell::new(vec![]),
});
let child2 = Rc::new(Node {
value: 0,
parent: RefCell::new(Weak::new()), // 最早的时候,无父级
children: RefCell::new(vec![]),
});
let now_pare = child1.parent.borrow().upgrade();
println!("{:#?}", now_pare); // None
// 向上指定父级
*(child1.parent.borrow_mut()) = Rc::downgrade(&root);
*(child2.parent.borrow_mut()) = Rc::downgrade(&root);
// root有两个弱引用,被放在了child1 和child2 内部
assert_eq!(Rc::weak_count(&root), 2);
let now_pare = child1.parent.borrow().upgrade();
println!("{:#?}", now_pare);
// Some(
//
Node {
//
value: 0,
//
parent: RefCell {
//
value: (Weak),
//
},
//
children: RefCell {
//
value: [],
//
},
//
},
// )
// 向下指定子节点
root.children.borrow_mut().push(Rc::clone(&child1));
root.children.borrow_mut().push(Rc::clone(&child2));
let now_pare = child1.parent.borrow().upgrade();
println!("{:#?}", now_pare);
// Some(Node {
//
value: 0,
//
parent: RefCell { value: (Weak) },
//
children: RefCell {
//
value: [
//
Node {
//
value: 0,
//
parent: RefCell { value: (Weak) },
//
children: RefCell { value: [] },
//
},
//
Node {
//
value: 0,
//
parent: RefCell { value: (Weak) },
//
children: RefCell { value: [] },
//
},
//
],
//
},
// })
}
// 弱引用的引用数
#[test]
fn show_weak_ref2() {
use std::rc::Rc;
let data = Rc::new(5);
assert_eq!(Rc::strong_count(&data), 1);
assert_eq!(Rc::weak_count(&data), 0);
let weak1 = Rc::downgrade(&data);
assert_eq!(Rc::weak_count(&data), 1);
let weak2 = Rc::downgrade(&data);
assert_eq!(Rc::weak_count(&data), 2);
let weak3 = Rc::downgrade(&data);
assert_eq!(Rc::weak_count(&data), 3);
drop(weak1);
assert_eq!(Rc::weak_count(&data), 2);
drop(weak2);
assert_eq!(Rc::weak_count(&data), 1);
drop(weak3);
assert_eq!(Rc::weak_count(&data), 0);
let weak1 = Rc::downgrade(&data);
assert_eq!(weak1.upgrade(), Some(Rc::new(5)));
drop(data);
assert_eq!(weak1.upgrade(), None);
}
the_enum.rs
use super::common::*;
#[test]
fn enum_value_as_value1() {
enum EnumValue {
_Zero, // 默认 == 0
_One,
_Two,
_Three,
}
let ev = EnumValue::_Zero;
assert_eq!(get_val_size(&ev), 1);
let ev = EnumValue::_Zero as u32;
assert_eq!(ev, 0);
}
#[test]
fn enum_value_as_value2() {
enum EnumValue {
_Zero = -100,
_One,
// 自动++
_Two,
// 自动++
_Three, // 自动++
}
let ev = EnumValue::_Three;
assert_eq!(get_val_size(&ev), 1);
let ev = EnumValue::_Three as i32;
assert_eq!(ev, -97);
}
#[test]
fn enum_value_as_value3() {
enum EnumValue {
_Zero,
_One = 102,
_Two = 1258,
_Three = 65535,
}
let ev = EnumValue::_Three;
assert_eq!(get_val_size(&ev), 2);
let ev = EnumValue::_Three as i32;
assert_eq!(ev, 65535);
}
#[test]
fn enum_value_as_value4() {
enum EnumValue {
_Zero = -1,
_One = 102,
_Two = 1258,
_Three = 65535,
}
let ev = EnumValue::_Three;
assert_eq!(get_val_size(&ev), 4);
}
#[test]
fn enum_value_as_value5() {
enum EnumValue {
_Zero,
_One,
_Two,
_Three = 65536,
}
let ev = EnumValue::_Three;
assert_eq!(get_val_size(&ev), 4);
}
#[test]
fn enum_value_as_value6() {
enum EnumValue {
_Zero = -129,
_One,
_Two,
_Three,
}
let ev = EnumValue::_Three;
assert_eq!(get_val_size(&ev), 2);
}
#[test]
fn enum_value_as_value7() {
enum EnumValue {
_Zero, // 仍旧默认为0
_One = -10,
_Two,
// ++ = -9
_Three, // ++ = -8
}
let ev = EnumValue::_Zero as i32;
assert_eq!(ev, 0);
let ev = EnumValue::_Two as i32;
assert_eq!(ev, -9);
}
#[test]
fn enum_use_mem_size3() {
enum EnumValue {
_Zero = 100,
_One = 102,
_Two = 1258,
_Three = 65536,
_Fout = 65537,
_Five = 0xffffffff + 1, // 超过u32上限
}
let e = EnumValue::_Zero;
assert_eq!(get_val_size(&e), 8);
}
// 无变体枚举定义
#[derive(Debug)]
enum _Info {
_One,
_Two,
_Three,
_Four,
_Five,
}
// 带变体枚举定义
#[derive(Debug)]
enum _Message {
_Quit,
_Move { _x: i32, _y: i32 },
// 变体为匿名结构体
_Write(char, char),
// 变体为元组
_ChangeColor(i32, i32, i32, i32), // 变体为元组
}
// 以上枚举_Message的本质:
struct _QuitMessage; // 类单元结构体
// 普通结构体
struct _MoveMessage {
x: i32,
y: i32,
}
// 元组结构体
struct _WriteMessage(char, char);
// 元组结构体
struct _ChangeColorMessage(i32, i32, i32);
// 枚举变体里包变体
#[derive(Debug)]
enum _Message2 {
_Msg(_Message),
_NoMsg,
}
// 枚举占用内存空间大小
#[test]
fn enum_base_usecase() {
let i = _Info::_One;
assert_eq!(get_val_size(&i), 1);
// 1 不带任何变体,占用1空间字节
let q = _Message::_Quit;
assert_eq!(get_val_size(&q), 20);
// 20 = 1->8 + 4 * 3
// 1被对齐的8字节用于记录枚举类型
// 4 * 3 是最高可能变体(ChangeColor)
// 枚举数组占用栈上空间
let arr = [_Message::_Quit, _Message::_Move { _x: 1, _y: 2 }];
assert_eq!(get_val_size(&arr), 40);
// 40 = 20 * 2
}
// 枚举也带所有权
#[test]
fn enum_ownership() {
let val1 = _Info::_Two;
let val2 = val1;
// println!("{:?}", val1); // 编译报错:value borrowed here after move
println!("{:?}", val2);
let val1 = _Message::_ChangeColor(0, 1, 2, 3);
let val2 = val1;
// println!("{:?}", val1); // 编译报错:value borrowed here after move
println!("{:?}", val2);
let val1 = _Message2::_Msg(_Message::_Write('我', '们'));
let val2 = val1;
// println!("{:?}", val1); // 编译报错:value borrowed here after move
println!("{:?}", val2);
}
// 枚举方法定义
impl _Message {
fn _say_hello(&self) -> &str {
// match 必须包含所有可能的情况
let enum_value = match self {
_Message::_Quit => "_Message::Quit",
_Message::_Move { _x, _y } => "_Message::Move",
_Message::_Write(_c1, _c2) => "_Message::_Write",
_Message::_ChangeColor(_i1, _i2, _i3, _i4) => "_Message::_ChangeColor",
};
enum_value
}
fn _say_hello2(&self) -> &str {
// _ 下划线就是C语言的 default:
let enum_value = match self {
_Message::_Quit => "_Message::Quit",
_ => "other",
};
enum_value
}
}
// 使用枚举的方法
#[test]
fn enum_function() {
let msg = _Message::_Quit;
assert_eq!(msg._say_hello(), "_Message::Quit");
let msg = _Message::_Write('你', '好');
assert_eq!(msg._say_hello(), "_Message::_Write");
}
thread_usecase.rs
use super::common::*;
// Rust 线程 由标准库来实现
// Rust 标准库只提供了 1:1 线程模型实现
// 你可以使用实现了 M:N 线程模型的 crate
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
// 使用thread::spawn新建线程,此线程为OS线程
// 主线程结束后,进程会结束,不会等其他线程
// thread::sleep的行为,依赖OS实现
#[test]
fn create_thread_case1() {
// 参数的闭包,是新线程入口
thread::spawn(|| {
for i in 0..10 {
println!("spawn thread! {}", i); // 0 1 2 3 4 打印不完,就结束了
thread::sleep(Duration::from_micros(2));
}
});
for i in 0..5 {
println!("main thread! {}", i); // 0 1 2 3 4
thread::sleep(Duration::from_micros(2));
}
}
// 主线程等待指定线程结束
// handle.join
#[test]
fn wait_for_thread_over() {
let handle = thread::spawn(|| {
for i in 0..10 {
println!("spawn thread! {}", i); // 0 1 2 3 4 5 6 7 8 9
thread::sleep(Duration::from_micros(2));
}
});
for i in 0..5 {
println!("main thread! {}", i); // 0 1 2 3 4
thread::sleep(Duration::from_micros(2));
}
// 此处同步等待
let result = handle.join();
assert_eq!(
get_var_type(&result),
"&core::result::Result<(), alloc::boxed::Box<dyn core::any::Any+core::marker::Send>>"
);
}
// 线程panic后,join将会返回Err
#[test]
#[should_panic]
fn thread_panic_handle_return_err() {
let handle = thread::spawn(|| {
panic!("thread error");
});
// 等待到了 Err
handle.join().unwrap();
}
// 新线程,强用主线程的变量,导致编译失败
#[test]
fn force_use_var_from_main_thread() {
let _v = vec![1, 2, 3];
// let handle = thread::spawn(|| {
//
println!("{:?}", _v);
// });
// handle.join().unwrap();
// 编译报错:
// closure may outlive the current function, but it borrows `_v`,
// which is owned by the current
// 闭包可能比本所在函数活得时间还长,但是却借用了_v,而_v被当前函数拥有。
// 当前函数结束后,_v被释放,但线程可能还没运行结束
}
// 闭包前,加上一个move,把v的所有权完全送进新线程
#[test]
fn send_ownership_to_thread() {
let v = vec![1, 2, 3];
// 闭包带关键字:move
let handle = thread::spawn(move || {
println!("{:?}", v);
});
handle.join().unwrap();
// 编译报错:
// borrow of moved value: `v`
// println!("{:?}", v);
}
// 线程间类似Go语言,发送消息
// mpsc:multiple producer, single consumer
// mpsc:多个生产者,单个消费者
// 简单场景:发送、接收成功
// channel send能够转移所有权
#[test]
fn send_msg_between_thread1() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let send_result = tx.send(String::from("hello!")).unwrap();
assert_eq!(send_result, ()); // 发送成功
});
let recv_result = rx.recv().unwrap();
assert_eq!(recv_result, String::from("hello!")); // 接收成功
}
// send用例:接收端既不recv也不drop,干等着,发送端发送大量数据,不会Err
#[test]
fn send_msg_between_thread2() {
let (tx, _rx) = mpsc::channel();
// 发送100W个u32,3.8MB
for _ in 0..100_0000 {
assert_eq!(tx.send(1024u32), Ok(()));
}
}
// send用例:接收端已drop,发送端send返回Err
#[test]
fn send_msg_between_thread3() {
let (tx, rx) = mpsc::channel();
drop(rx);
assert_ne!(tx.send(1024u32), Ok(()));
}
// recv用例:发送端直接drop,接收端recv直接返回Err
#[test]
fn send_msg_between_thread4() {
let (tx, rx) = mpsc::channel::<i32>();
drop(tx);
if let Err(_) = rx.recv() {
assert_eq!(1, 1);
} else {
assert_eq!(1, 2);
}
}
// recv用例:发送端直接drop,接收端try_recv返回Err
#[test]
fn send_msg_between_thread5() {
let (tx, rx) = mpsc::channel::<i32>();
drop(tx);
if let Err(_) = rx.try_recv() {
assert_eq!(1, 1);
} else {
assert_eq!(1, 2);
}
}
// recv用例:发送端发送数据后drop,接收端recv返回Err,之前发的数据不会丢失
#[test]
fn send_msg_between_thread6() {
let (tx, rx) = mpsc::channel();
for i in 0..5 {
tx.send(i).unwrap();
}
drop(tx);
assert_eq!(rx.recv(), Ok(0));
assert_eq!(rx.recv(), Ok(1));
assert_eq!(rx.recv(), Ok(2));
assert_eq!(rx.recv(), Ok(3));
assert_eq!(rx.recv(), Ok(4));
// 再接就err
if let Err(_) = rx.recv() {
assert_eq!(1, 1);
} else {
assert_eq!(1, 2);
}
}
// recv用例:发送端发送数据后drop,接收端try_recv返回Err,之前发的数据不会丢失
#[test]
fn send_msg_between_thread7() {
let (tx, rx) = mpsc::channel();
for i in 0..5 {
tx.send(i).unwrap();
}
drop(tx);
assert_eq!(rx.try_recv(), Ok(0));
assert_eq!(rx.try_recv(), Ok(1));
assert_eq!(rx.try_recv(), Ok(2));
assert_eq!(rx.try_recv(), Ok(3));
assert_eq!(rx.try_recv(), Ok(4));
// 再接就err
if let Err(_) = rx.try_recv() {
assert_eq!(1, 1);
} else {
assert_eq!(1, 2);
}
}
// recv用例:发送端未drop,但不提供内容,try_recv返回Err
#[test]
fn send_msg_between_thread8() {
let (_tx, rx) = mpsc::channel::<i32>();
if let Err(_) = rx.try_recv() {
assert_eq!(1, 1);
} else {
assert_eq!(1, 2);
}
}
// recv用例:接收端recv卡住,过一会儿发送端drop,接收端被卡住的recv立即返回Err
#[test]
fn send_msg_between_thread9() {
let (tx, rx) = mpsc::channel::<i32>();
thread::spawn(move || {
thread::sleep(Duration::from_millis(10));
drop(tx); // 过一会儿后drop tx
});
// 过一会儿后,Err
if let Err(_) = rx.recv() {
assert_eq!(1, 1);
} else {
assert_eq!(1, 2);
}
}
// 对rx使用迭代器
#[test]
fn channel_iter() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let vals = vec![0, 1, 2, 3, 4, 5];
for val in vals {
tx.send(val).unwrap();
}
});
let mut guess_num = 0;
// 迭代器已经把rx消耗掉了
// 当从rx中读取Err时,迭代结束
for num in rx {
assert_eq!(num, guess_num);
guess_num += 1;
}
// println!("{:?}", rx);
// rx编译错误:borrow of moved value: `rx`
}
// 可以使用clone,使拥有多个sender
#[test]
fn clone_sender() {
let (tx, rx) = mpsc::channel();
let tx1 = mpsc::Sender::clone(&tx);
tx.send(1).unwrap();
tx1.send(2).unwrap();
// 接收顺序有保证
assert_eq!(rx.recv(), Ok(1));
assert_eq!(rx.recv(), Ok(2));
}
// 使用所,共享内存
// Arc是原子引用计数(atomically reference counted),是Rc的多线程版
// Arc<T> 和 Rc<T> 有着相同的 API
// Mutex是RefCell的多线程版
#[test]
fn use_mutex() {
let data = Arc::new(Mutex::new(0));
let data1 = Arc::clone(&data);
thread::spawn(move || {
for _ in 0..100 {
let mut inner_num = data1.lock().unwrap();
*inner_num = 1; // 加锁
} // <-- drop 解锁
});
let data2 = Arc::clone(&data);
thread::spawn(move || {
for _ in 0..100 {
let mut inner_num = data2.lock().unwrap();
*inner_num = 2;
}
});
thread::sleep(Duration::from_micros(50));
let now_val = *data.lock().unwrap();
assert!(now_val == 1 || now_val == 2, "{}", now_val);
}
// 如果另一个线程拥有锁,并且那个线程 panic 了,则 lock 调用会失败
#[test]
#[should_panic]
fn use_mutex_panic() {
let data = Arc::new(Mutex::new(0));
let data1 = Arc::clone(&data);
thread::spawn(move || {
let mut inner_num = data1.lock().unwrap();
*inner_num = 1;
panic!(); // 这里panic了,没有释放锁
});
thread::sleep(Duration::from_micros(50));
let _now_val = *data.lock().unwrap();
}
// Send trait
// Send 标记 trait 表明类型的所有权可以在线程间传递
// 几乎所有的 Rust 类型都是Send 的
// 任何完全由 Send 的类型组成的类型也会自动被标记为 Send
// 不是Send: 裸指针(raw pointer)、Rc<T>
// Sync trait
// Sync 允许多线程访问
// 基本类型是 Sync 的,完全由 Sync 的类型组成的类型也是 Sync 的
// 手动实现 Send 和 Sync 是不安全的
// 通常并不需要手动实现 Send 和 Sync trait
// 因为由 Send 和 Sync 的类型组成的类型,自动就是 Send 和 Sync 的
types.rs
use super::common::*;
// 整型
// 长度
有符号
无符号
// 8-bit
i8
u8
// 16-bit
i16
u16
// 32-bit
i32
u32
// 64-bit
i64
u64
// 128-bit
i128
u128
// arch
isize
usize
// arch: 64 位架构上它们是 64 位的, 32 位架构上它们是 32 位的
// 数字字面值
例子
// Decimal (十进制)
98_222
// Hex (十六进制)
0xff
// Octal (八进制)
0o77
// Binary (二进制)
0b1111_0000
// Byte (单字节字符)(仅限于u8)
b'A'
// 浮点 f32 f64
// 布尔 bool: true, false
// 字符 char: 4字节, utf8
// 字符串 str
// 元组 (6,5,4)
// 数组 [6,5,4]
// debug模式下编译的程序,整型溢出发生时,将会panic
// release模式下,整型溢出则发生补码反转
// rust语言中,依赖整型溢出被认为是一种错误,标准库中有一个类型显式提供此功能,Wrapping
// 变量默认不可变
#[test]
fn var_default_immutable() {
let x = 5; // 变量默认不可变,其他大多数语言都是默认变量是可变的
// 其他语言提供不可变约束,C语言提供const,C#提供readonly,但不默认。
// 亮点:强迫用户必须显式声明可变的变量,读代码时,显式可见可变性。
assert_eq!(x, 5);
// x = 6; // 编译错误,x不具备可变性
let mut y = 5; // 可变变量,需要显式加mut关键字
assert_eq!(y, 5); // 不加这一行,会编译告警,编译器认为初始化值=5没用。
y = 6;
assert_eq!(y, 6);
}
// 变量隐藏
#[test]
fn var_let_shadowing() {
let x = 5;
assert_eq!(x, 5);
// 再次let同名变量,会隐藏之前的变量,数据类型可变
let x = 0.5; // 之前的 == 5 的x完全丢了,以后就是 == 0.5的新x
let x = x + 0.2; // 隐藏之前还可以用一把
assert_eq!(x, 0.7);
}
// 字面量自带默认类型
#[test]
fn var_default_type() {
// 整形字面量默认是i32类型,字面值越了i32的界就编译失败
// assert_eq!(get_var_type(0xffffffff), "i32"); // 此行代码报错
// C语言会视字面量大小,自动调整类型,类型规则各实现还不一致。
// 亮点: 保证整形字面量绝逼是i32,不会自动转换。规则简单暴力,不用太多思考,不易出错。
assert_eq!(get_var_type(5), "i32");
assert_eq!(get_var_type(0xFF), "i32");
assert_eq!(get_var_type(0o77), "i32");
assert_eq!(get_var_type(0b1001), "i32");
assert_eq!(get_var_type(0.5), "f64");
assert_eq!(get_var_type(true), "bool");
assert_eq!(get_var_type('c'), "char");
assert_eq!(get_var_type(b'c'), "u8");
assert_eq!(get_var_type("my name is xxx"), "&str"); // 字符串类型
assert_eq!(get_var_type((5, 6, 7)), "(i32, i32, i32)"); // 元素,包含三个并列的i32
assert_eq!(get_var_type([5, 6, 7]), "[i32; 3]"); // 数组,类型为i32,长度为3
}
// 使用数字表示char
#[test]
fn use_num_as_char() {
let _c: char = 'x7F';
// let _c: char = 'xFF';
// 编译报错:
// this form of character escape may only be used with characters in the range [x00-x7f]
// 即最大只能表示127
let _c: char = 'u{FF0AB}';
let _c: char = 'u{FF0ab}';
// let _c: char = 'u{FFFFFFFF}';
// 编译报错: overlong unicode escape (must have at most 6 hex digits)
}
// 字面量后缀确定变量类型
#[test]
fn var_define_type() {
let x = 5f32;
assert_eq!(get_var_type(x), "f32");
let x = 5u64;
assert_eq!(get_var_type(x), "u64");
let x = 5i128;
assert_eq!(get_var_type(x), "i128");
let x = (5u8, 6u16, 7u32);
assert_eq!(get_var_type(x), "(u8, u16, u32)");
}
// 数值运算与类型推导,rust严格要求参与计算的数据类型一致性
// 亮点:数值计算不允许任何隐式数据类型转换,简单暴力,不会因隐式数据类型转换导致隐藏bug
#[test]
fn value_caculation() {
let x = 0.2;
let y = 0.3f32;
assert_eq!(x + y, 0.5);
assert_eq!(get_var_type(x), "f32"); // 编译器由x + y推导出x也是f32类型的,而非默认的f64
let x = 5;
let y = 5u8;
assert_eq!(x + y, 10); // 编译器由x + y推导出x也是u8类型的,而非默认的i32
assert_eq!(get_var_type(x), "u8");
// assert_eq!(5u8 + 5u16, 10); // 编译失败: no implementation for `u8 + u16` 赞!
}
// 数组基本用法
#[test]
fn use_array1() {
let x = 1;
let arr = [0, 1, 2, 3, 4, 5, 6];
let y = 2;
// 局部数组,放在栈上,内存连续
println!("x address
{:p}", &x); // 0xba59cff884
println!("arr address {:p}", &arr); // 0xba59cff888 +4
println!("y address
{:p}", &y); // 0xba59cff8a4 合理 0xa4-0x88=28
28/4=7
// 对数组类型的描述,类型i32,数量7 [类型; 数量]
assert_eq!(get_var_type(arr), "[i32; 7]");
// 语法糖,数组初始化写法,100个0.5f32,其中数量必须为usize类型,否则编译报错
let arr2 = [0.5f32; 100usize];
assert_eq!(get_var_type(arr2), "[f32; 100]");
for &elem in arr2.iter() {
assert_eq!(elem, 0.5);
}
// 这里i被推导成usize,数组索引只支持usize
// 亮点:不允许i32 u32等其他整形作数组索引,规则简单单一不易出错
for i in 1..100 {
assert_eq!(arr2[i], 0.5);
assert_eq!(get_var_type(i), "usize");
}
// let x = 100;
// let arr = [0.5f32; x]; // non-constant valuerustc(E0435) Rust不支持类似C99的变长数组VLA(variable-length array)
}
// Rust区分表达式和语句,不能像下面一样写。
// C语言: int x = 5; 这是个赋值表达式,赋值动作只是表达式的副作用,表达式的值为5,所以可以这么写: int y = (x = 5);
// 亮点:不允许太骚,写各种连续赋值表达式,可读性好。
// 类型别名(type alias)
#[test]
fn use_type_alias() {
// Kilometers 是 i32 的 同义词(synonym)
type Kilometers = i32;
let x: i32 = 5;
let y: Kilometers = 5;
println!("x + y = {}", x + y);
}
// std::io 有这个类型别名声明:
// type Result<T> = std::result::Result<T, std::io::Error>;
// 使其书写简单,画面整洁
// 栈上只能存储静态大小类型实例
// 动态大小类型的黄金规则:必须将动态大小类型的值置于某种指针之后
// 动态大小的类型:trait、str
// 确定大小的类型:dyn trait、String、i32、bool、Vec
// 强制类型转换
#[test]
fn use_as() {
let x: i32 = -1;
let y = x as u32; // 这个as类似C语言的强转,内存完全不变,补码
assert_eq!(y, 0xffffffff);
}
unit_test.rs
#[test]
fn test_self_case1() {
assert!(2 == 2);
}
#[test]
fn test_self_case2() {
let x = 2;
let y = 2;
assert!(x == y, "{} == {}", x, y); // 用例失败时,才会打印format + val
}
#[test]
fn test_self_case3() {
let x = 2;
let y = 2;
assert_eq!(x, y, "{} == {}", x, y); // 用例失败时,才会打印format + val
}
#[test]
#[should_panic] // 必须发生panic,才能通过本用例
fn test_self_case4() {
panic!("???");
}
#[cfg(test)] // 只在执行 cargo test 时才编译和运行测试代码,而在运行 cargo build 时不这么做
mod tests {
#[test] // 测试函数的返回值也可以是Result,如果返回Err(变体),则算用例失败
// 这样编写测试来返回 Result<T, E> 就可以在函数体中使用问号运算符,如此可以方便的编写任何运算符会返回 Err 成员的测试。
fn it_works() -> Result<(), String> {
if 2 + 2 == 4 {
Ok(())
} else {
Err(String::from("two plus two does not equal four"))
}
}
}
#[test]
#[ignore] // 默认运行cargo test时是忽略的,除非执行 cargo test -- --ignored
fn test_use_much_time() {
use std::thread;
use std::time::Duration;
thread::sleep(Duration::from_secs(10)); // 需要执行10秒
assert_eq!(1, 1);
}
unsafe_rust.rs
use super::common::*;
// 不安全的超级力量:
// 1、解引用裸指针
// 2、调用不安全的函数或方法
// 3、访问或修改可变静态变量
// 4、实现不安全 trait
// 5、访问 union 的字段
// 不可变裸指针: *const T
//
可变裸指针: *mut T
// 裸指针的特点:(理解成C语言的指针即可)
// 1、允许忽略借用规则,可以同时拥有不可变和可变的指针,或多个指向相同位置的可变指针
// 2、不保证指向有效的内存
// 3、允许为空
// 4、不能实现任何自动清理功能
// 可以获取裸指针,只要不适用,就不需要unsafe标记
// rust编译器足够智能,知道用户只是申请了但没用,就不报错
// 解引用 裸指针才需要unsafe块
#[test]
fn use_native_ptr1() {
let mut num = 5;
let _r1 = &num as *const i32;
let _r2 = &mut num as *mut i32;
assert_eq!(get_var_type(_r1), "*const i32");
assert_eq!(get_var_type(_r2), "*mut i32");
// 指针的地址完全一致
println!("{:p}", &num); // 0x3d10efd35c
println!("{:p} ", _r1); // 0x3d10efd35c
println!("{:p} ", _r2); // 0x3d10efd35c
// *_r2 = 6;
// 编译报错:dereference of raw pointer is unsafe and requires unsafe function or block
}
#[test]
fn use_native_ptr2() {
let _r1;
let _r2;
{
let mut num = 5;
_r1 = &num as *const i32;
_r2 = &mut num as *mut i32;
println!("{:p}
", &num); // 0x55602fd5c
} // <-- 通过本用例获知,实际上num所占用的栈空间,在这里没有被释放
let _num2 = 10;
println!("{:p}
", _r1); // 0x55602fd5c 8
println!("{:p}
", _r2); // 0x55602fd5c 8 +0
println!("{:p}", &_num2); // 0x55602fd5c c +4
unsafe {
assert_eq!(*_r1, 5);
*_r2 = 100; // num的作用于都丢了,这里还能解引用,果然很危险啊
}
}
// C/C++最常见的空指针,引发段错误Segmentation fault,进程异常退出
#[test]
fn use_native_ptr3() {
let address = 0x0;
let _r = address as *mut i32;
// 引发段错误Segmentation fault
// unsafe {
//
*_r = 100;
// }
}
// 调用不安全函数,必须在unsafe块中
#[test]
fn use_native_block1() {
unsafe fn heheda() {
println!("hello!");
}
unsafe {
heheda();
}
// heheda();
// call to unsafe function is unsafe and requires unsafe function or block
}
// 封装unsafe块
#[test]
fn use_native_block2() {
fn heheda() {
unsafe {
let mut x = 10;
let px = &mut x as *mut i32;
*px = 100;
assert_eq!(x, 100);
}
}
heheda();
}
// 一个正常使用unsafe rust代码的场景:
// 把一个mut切片,分成两个mut切片
// 不用unsafe的话,不允许同时存在 指向一个位置的 两个可变切片
#[test]
fn use_native_block3() {
use std::slice;
fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = slice.len();
let ptr = slice.as_mut_ptr();
assert!(mid <= len);
unsafe {
(
// slice::from_raw_parts_mut 函数获取一个裸指针和一个长度来创建一个 slice
slice::from_raw_parts_mut(ptr, mid),
slice::from_raw_parts_mut(ptr.add(mid), len - mid),
)
}
}
let mut v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
let (v1, v2) = split_at_mut(&mut v, 3);
assert_eq!(v1, &[1, 2, 3]);
assert_eq!(v2, &[4, 5, 6, 7, 8, 9]);
v1[0] += 1;
v2[0] += 1;
assert_eq!(v1, &[2, 2, 3]);
assert_eq!(v2, &[5, 5, 6, 7, 8, 9]);
}
// 实模式下,往指定地址块,写数据,完成IO
#[test]
fn use_native_block4() {
use std::slice;
let address = 0x01234567usize; // 这里开始是网卡地址的映射,长度为10000个字节
let r = address as *mut u32;
let _slice: &mut [u32] = unsafe {
println!("这里是约定好的映射位置");
slice::from_raw_parts_mut(r, 10000)
};
// Segmentation fault
// _slice[0] = 0xffffffff;
}
// 调用C语言的函数,是不安全的
// 这里rustc直接用了gcc的链接器
#[test]
fn use_native_block5() {
extern "C" {
fn abs(input: i32) -> i32; // libc.so 里的函数
fn htons(input: u16) -> u16; // libc.so 里的函数
}
unsafe {
assert_eq!(abs(-3), 3);
assert_eq!(htons(0x491c), 0x1c49);
}
}
// 全局变量在 Rust 中被称为 静态(static)变量
// 因为全局变量的生命周期,必须是'static的
// 全局变量只能储存拥有 'static 生命周期的引用
// 注意1:命名风格
// 注意2:必须类型标注
// 注意3:必须带static关键字,说明是一个全局静态变量
static HELLO_WORLD: &str = "Hello, world!";
#[test]
fn use_global_var1() {
// 访问不可变静态变量是安全的
assert_eq!(HELLO_WORLD, "Hello, world!");
}
// 全局变量
// 静态生命周期
// 类型为u32
// 可变
static mut COUNTER: u32 = 0;
// 读取或修改一个可变静态变量是不安全的
// 要用unsafe
#[test]
fn use_global_var2() {
unsafe {
COUNTER += 1;
}
}
// 不安全trait
unsafe trait Foo {
// methods go here
}
// 实现不安全trait
unsafe impl Foo for i32 {
// method implementations go here
}
// union 如同C语言的 union
#[test]
fn use_union() {
union Data {
d1: u32,
d2: (u8, u8, u8, u8),
};
let data = Data { d1: 0xffeeddcc };
// 读取或设置union成员,必须使用unsafe代码块
unsafe {
assert_eq!(data.d2.0, 0xcc);
assert_eq!(data.d2.1, 0xdd);
assert_eq!(data.d2.2, 0xee);
assert_eq!(data.d2.3, 0xff);
}
}
use_string.rs
use super::common::*;
// println!打印String效果
// format!效果,返回值类型:alloc::string::String
#[test]
fn println_string_effrct() {
let name = String::from("zhang xiao san");
let print_result1 = format!("{}", &name);
let print_result2 = format!("{}", name);
assert_eq!(print_result1, print_result2); // 这里不会消耗所有权
assert_eq!(get_var_type(print_result1), "alloc::string::String"); // get_var_type 里 Move and free
// println!("{}", print_result1); // print_result1 编译报错: borrow of moved value: `print_result1`
}
// 字符串切片
// &str本质上是字符串切片
// 直接字符串字面量,如"hello world",类型就是&str,但&str和String实现不一样
//
字面量的ptr直接指向了静态内存,而String的ptr指向的数据是存储在堆上
//
指向String的&str切片,String离开作用域时会被free
//
rust编译器会保证String离开作用域后,指向其的切片不会再被使用
// 编译器会确保指向 String 的 &str 在 String释放前有效。
#[test]
fn string_slice_usecase() {
// 堆上malloc一块内存m1,存储"hello world"
// s的ptr指向m1的'h',其cap = 11+, len = 11
let s = String::from("hello world");
// 此切片的ptr指向m1的'h',len = 5
let hello = &s[0..5];
// 此切片的ptr指向m1的'w',len = 5
let world = &s[6..11];
assert_eq!(get_var_type(hello), "&str");
assert_eq!(get_var_type(world), "&str");
// 切片的边界规则
let s = String::from("hello");
let slice = &s[0..2]; // 相当于[0..2) 前闭后合
assert_eq!(slice, "he");
// 下面2行会导致编译报错: cannot borrow `s` as mutable because it is also borrowed as immutable
// s.clear();
// println!("{}", slice);
// 因为:&s[0..2] slice &str,自身相当于s的不可变引用,clear内部产生了s的可变应用,违反了所有权借用规则2。
// 如果clear后面不再用&str slice了,就编译ok
// 即:编译器保证&str的有效性,不能在clear s之后,再用&str
let slice = &s[0..=2]; // 相当于[0..2] 前闭后闭,len=3
assert_eq!(slice, "hel");
let slice = &s[..2]; // 前面不写的默认是0
assert_eq!(slice, "he");
let slice = &s[0..]; // 后面不写默认是len(array)
assert_eq!(slice, "hello");
let slice = &s[..]; // 前后都不写,相当于[0..len(array))
assert_eq!(slice, "hello");
}
#[test]
fn use_string_1() {
let data = "initial contents";
assert_eq!(get_var_type(data), "&str");
let s1 = data.to_string();
assert_eq!(get_var_type(&s1), "&alloc::string::String");
// 该方法也可直接用于字符串字面值:
let s2 = "initial contents".to_string();
assert_eq!(s1, s2);
}
#[test]
fn use_string_2() {
// 遍历字符串1
let _hello = "你好";
// 不会夺取所有权
for c in _hello.chars() {
println!("{}", c); // 你好
}
// 不会夺取所有权
for c in _hello.bytes() {
println!("{}", c); // 228 189 160 229 165 189
}
// 遍历字符串2
let _hello = String::from("你好");
// 不会夺取所有权
for c in _hello.chars() {
println!("{}", c); // 你好
}
// 不会夺取所有权
for c in _hello.bytes() {
println!("{}", c); // 228 189 160 229 165 189
}
}
#[test]
fn use_string_3() {
let _hello = String::from("你好");
assert_eq!(_hello.len(), _hello.bytes().len()); // String.len() 就是占用字节数
// let h = _hello[0]; // 编译报错:the type `std::string::String` cannot be indexed by `{integer}`
// 因为utf-8
}
#[test]
fn use_string_4() {
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
// + 操作符函数: fn add(self, s: &str) -> String
// 它看起来好像生成了很多拷贝,不过实际上并没有
// 之所以能够在 add 调用中使用 &s2 是因为 &String 可以被 强转 成 &str
// 即:&s2 变成了 &s2[..],取其全量切片
let _s3 = s1 + &s2; // 注意 s1 被Move给 s3 了,不能继续使用
// println!("{}", s1); // 编译报错,borrow of moved value: `s1`
}
#[test]
fn use_string_5() {
// format! 与 println! 的工作原理相同,不过不同于将输出打印到屏幕上,它返回一个带有结果内容的 String
// 这个版本就好理解的多,并且不会获取任何参数的所有权。
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let _s3 = format!("{}{}", s1, s2);
println!("{}", s1); // s1 还能用
}
use_struct.rs
use super::common::*;
// 结构体User定义
// 注意:这里User自身拥有自己的所有权,User下每个字段都完整拥有自己数据的拥有权。
// 如果name: &str,将会复杂。需要了解并处理生命周期问题。
// 先基于简单的场景,学习结构体。
#[derive(Debug)] // 此注解使结构体默认实现了 Debug 接口(trait),允许string format"{:?}"打印,打印出来是这样的:
// User { name: "wang wu", email: "690313521@qq.com", age: 31, money: 100, is_vip: false, is_online: false }
struct User {
name: String,
email: String,
age: u8,
money: u64,
is_vip: bool,
is_online: bool,
}
// 结构体基本操作
#[test]
fn create_struct_usecase() {
// 必须把结构体下的所有字段都初始化,否则实例化失败,编译报错
let mut usr1 = User {
name: String::from("zhang san"),
email: String::from("690313521@qq.com"),
age: 31,
money: 100,
is_vip: false,
is_online: true,
};
usr1.is_online = false;
// 基于已有实例实例化,除了name、emali,其余copy自usr1
let usr2 = User {
name: String::from("li si"),
email: String::from("xxx@163.com"),
..usr1
};
assert_eq!(usr2.email, "xxx@163.com");
// 下面这个过程发生了usr1的email的所有权move
let usr3 = User {
name: String::from("wang wu"),
..usr1
};
// assert_eq!(usr1.email, "690313521@qq.com"); 编译报错,因为usr1.email已经被move给usr3了
assert_eq!(usr3.email, "690313521@qq.com");
// println!("{:?}", &usr1); // 编译报错:value borrowed here after partial move,usr1部分字段所有权被move了
println!("{:?}", &usr3);
}
// 元组结构体定义:
// 元组结构体与常规结构体区别:没有具体的字段名,只有字段的类型
// 元组结构体与常规元组的区别:有结构体名称、有独立类型
pub struct Color(i32, i32, i32);
pub struct Point(i32, i32, i32);
// 元组结构体定义特点
#[test]
fn tuple_struct_usecase() {
// 元组结构体:
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
assert_eq!(get_var_type(&black), "&hello_rust::use_struct::Color"); // 这里如果不带&,直接传,就move了
assert_eq!(get_var_type(&origin), "&hello_rust::use_struct::Point"); // 这里如果不带&,直接传,就move了
// 支持.x索引方式获取值
assert_eq!(black.0, 0);
// black = origin; // 编译报错,不是同一类型,不能互相赋值。
// 普通元组:
let mut black = (0, 0, 0);
let origin = (1, 1, 1);
assert_eq!(get_var_type(black), "(i32, i32, i32)");
assert_eq!(get_var_type(origin), "(i32, i32, i32)");
// 普通元组类型一致,可以赋值
black = origin;
assert_eq!(black.1, 1);
}
#[derive(Debug)]
pub struct Rectangle {
width: u32,
height: u32,
}
// 方法定义:
impl Rectangle {
// 本大括号内的所有self,都指代Rectangle
// 入参为不可变引用
pub fn _area1(&self) -> u32 {
self.height * self.width
}
// 入参为可变引用
pub fn _area2(&mut self) -> u32 {
self.height += 1; // 可变
self.height * self.width
}
// 调用此方法会发生数据拥有权的传递
// 方法获取实例的所有权是很少见;这种技术通常用在当方法将 self 转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例。
pub fn _area3(self) -> u32 {
self._area1()
} // self拥有的内存变得无效了、不能再调用了。
}
// 可针对同一个结构体,有多个impl块
impl Rectangle {
pub fn _area4() -> u32 {
return 0;
}
pub fn _can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
// 在impl块中定义不以self作为参数的函数,这被称为关联函数
// 是函数而不是方法,用法:let sq = Rectangle::square(3);
pub fn _make_square(size: u32) -> Rectangle {
let sq = Rectangle {
width: size,
height: size,
};
println!("make_square
{:p}", &sq); // 0x71fa75d 340
println!("make_square {:p}", &sq.width); // 0x71fa75d 340 +0
println!("make_square{:p}", &sq.height); // 0x71fa75d 344 +4
sq // 这里发生了Move
}
}
// 结构体数据存储在栈上
// 调用栈越深,地址值越小
#[test]
fn struct_address_usecase() {
let size = 1000;
let sq = _show_struct_address();
println!("struct_address_usecase
{:p}", &size); // 0x71fa75d 78c
println!("struct_address_usecase
{:p}", &sq); // 0x71fa75d 790 +4
println!("struct_address_usecase {:p}", &sq.width); // 0x71fa75d 790 +0
println!("struct_address_usecase{:p}", &sq.height); // 0x71fa75d 794 +4
}
pub fn _show_struct_address() -> Rectangle {
let size = 10;
let sq = Rectangle::_make_square(size);
println!("show_struct_address
{:p}", &size); // 0x71fa75d 4f4
println!("show_struct_address
{:p}", &sq); // 0x71fa75d 4e8 +4
println!("show_struct_address {:p}", &sq.width); // 0x71fa75d 4e8 +0
println!("show_struct_address{:p}", &sq.height); // 0x71fa75d 4ec +4
sq
}
use_trait.rs
// trait基本用法
#[test]
fn show_trait_base() {
trait Summary {
fn summarize(&self) -> String; // 接口方法1:制作摘要
// 接口方法2:这条推,火不火,被默认实现
fn is_hot(&self) -> bool {
println!("{}", self.summarize()); // 默认实现里,可以调用未来会实现的方法
false // 接口默认实现
}
}
// 一条推特
struct TweetItem {
username: String,
content: String,
}
// 为 TweetItem 实现 Summary trait
// 必须实现接口内所有 没默认实现的 方法,否则报错:
// not all trait items implemented, missing: `summarize`
impl Summary for TweetItem {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
// is_hot 这个接口可以实现,也可以不实现。
// is_hot 不实现,则使用默认方法
}
let item1 = TweetItem {
username: String::from("zhang xiao san"),
content: String::from("heheda"),
};
// 像正常方法一个使用
assert_eq!(item1.summarize(), "zhang xiao san: heheda");
// 执行接口默认实现
let _yes = item1.is_hot();
}
#[test]
fn show_trait_base2() {
trait Summary {
fn summarize(&self) -> String;
}
struct TweetItem {
username: String,
content: String,
}
impl Summary for TweetItem {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
// notify1 和 notify2,一毛一样,都是静态分发
fn _notify1(item: impl Summary) {
println!("Breaking news! {}", item.summarize());
}
fn _notify2<T: Summary>(item: T) {
println!("Breaking news! {}", item.summarize());
}
// notify3 和 notify4 一毛一样,都是静态分发
fn _notify3(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
fn _notify4<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
let tt = TweetItem {
username: String::from("zhang san"),
content: String::from("heheda"),
};
_notify4(&tt);
_notify3(&tt);
_notify2(tt); // Move, free
// _notify1(tt); // 编译错误: use of moved value: `tt`
// trait作为参数,两种写法的区别:
// 1中,不要求item1和item2实际类型一致,只要都实现了接口即可。
// 2中,要求item1和item2实际类型一致,都是T。
// pub fn notify1(item1: impl Summary, item2: impl Summary)
// pub fn notify2<T: Summary>(item1: T, item2: T)
// 3 == 4
// pub fn notify3(item: impl Summary + Display)
// pub fn notify4<T: Summary + Display>(item: T)
// 5 == 6
// fn some_function5<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {
// fn some_function6<T, U>(t: T, u: U) -> i32
//
where T: Display + Clone,
//
U: Clone + Debug
// {
fn _returns_summarizable() -> impl Summary {
TweetItem {
username: String::from("horse_ebooks"),
content: String::from("of course, as you probably already know, people"),
}
}
// 编译报错:
// the size for values of type `dyn show_trait_base2::Summary` cannot be known at compilation time
// item 即将被存储在栈上,但是编译时不确定其尺寸,因为不确定函数返回的是什么实际类型,所以不支持这么写。
// let item: Summary = _returns_summarizable();
}
// 有条件的实现方法
#[test]
fn generic_function_condition() {
use std::fmt::Display;
struct _Pair<T> {
x: T,
y: T,
}
impl<T> _Pair<T> {
fn _new(x: T, y: T) -> Self {
Self { x, y }
}
}
// 只为满足T: Display + PartialOrd的泛型_Pair实现_cmp_display方法
impl<T: Display + PartialOrd> _Pair<T> {
fn _cmp_display(&self) {
if self.x >= self.y {
println!("The largest member is x = {}", self.x);
} else {
println!("The largest member is y = {}", self.y);
}
}
}
}
// 对任何实现了特定 trait 的类型有条件地实现 trait
#[test]
fn generic_function_condition2() {
// 接口1
trait WhatYouSay1 {
fn say1(&self) -> String;
}
// 接口2
trait WhatYouSay2 {
fn say2(&self) -> String;
}
// 结构体
struct MyType {}
// 为 结构体 实现接口1
impl WhatYouSay1 for MyType {
fn say1(&self) -> String {
return String::from("你瞅啥");
}
}
// 对任何实现了 接口1 的类型有条件地实现 接口2
impl<T: WhatYouSay1> WhatYouSay2 for T {
fn say2(&self) -> String {
return String::from("瞅你咋滴");
}
}
let x = MyType {};
assert_eq!(x.say1(), "你瞅啥");
assert_eq!(x.say2(), "瞅你咋滴");
// 下面写法:3.to_string()
// 原因是,标准库中为所有实现了Display的类型实现了ToString:
// impl<T: std::fmt::Display + ?Sized> ToString for T {
let s = 3.to_string();
assert_eq!(s, "3");
}
// 实现 trait 时需要注意的一个限制:
// 只有当 <trait本身> 或者要实现 trait 的 <类型> 位于 crate 的本地作用域时,才能为该类型实现 trait
// 不能为外部类型实现外部 trait
// 孤儿原则
// 简单的用元素()把其他 crate 的类型包装起来,形成一个新类型后,可以实现 trait,打破孤儿原则
// 多态方法:
// 1、使用枚举变体:编译时,确定对象尺寸
// 2、使用trait对象:编译时,不确定对象尺寸
trait Animal {
fn run(&self);
}
struct Tiger {
_name: String,
}
impl Animal for Tiger {
fn run(&self) {
println!("┗|`O′|┛ 嗷~~");
}
}
struct Cat {
_age: f32,
}
impl Animal for Cat {
fn run(&self) {
println!("喵");
}
}
struct Dog {
_is_happy: bool,
}
impl Animal for Dog {
fn run(&self) {
println!("旺旺");
}
}
// Trait 对象要求对象安全
// 对象安全要求1:返回值类型不为 Self
// 对象安全要求2:方法没有任何泛型类型参数
#[test]
fn use_trait_object1() {
let mut my_pets: Vec<Box<dyn Animal>> = vec![]; // trait对象数组
my_pets.push(Box::new(Tiger {
_name: String::from("猫山王"),
}));
my_pets.push(Box::new(Cat { _age: 0.32f32 }));
my_pets.push(Box::new(Cat { _age: 1.32f32 }));
my_pets.push(Box::new(Dog { _is_happy: false }));
for pet in my_pets.iter() {
pet.run(); // 动态分发,运行时查方法表
}
// trait内部实现是两个指针
// 一个指向了具体的数据(每个实例一份)
// 一个指向其函数表(全局只有一份)
// 原型:
pub struct _TraitObject {
pub data: *mut (),
// 原生元组可变指针
pub vtable: *mut (), // 原生元组可变指针
}
println!("{:p}", &my_pets); // 0x8bf118d408 栈上地址
println!("{:p}", &my_pets[0]); // 0x255c19981 60 堆上地址
println!("{:p}", &my_pets[1]); // 0x255c19981 70 堆上地址 +16
println!("{:p}", &my_pets[2]); // 0x255c19981 80 堆上地址 +16
println!("{:p}", &my_pets[3]); // 0x255c19981 90 堆上地址 +16
}
// 关于动态分发与静态分发
#[test]
fn about_trait_static_dy_invoke() {
trait Animal {
fn get_age(&self) -> i32;
}
struct Dog {
age: i32,
}
impl Animal for Dog {
fn get_age(&self) -> i32 {
self.age
}
}
// 这里是泛型的,静态分发
// 实际编译类型会是实际类型
fn static_invoke<T>(t: T)
where
T: Animal,
{
assert_eq!(t.get_age(), 10);
}
// 动态分发1
fn dynamic_invoke1(t: &dyn Animal) {
assert_eq!(t.get_age(), 10);
}
// 动态分发2
fn dynamic_invoke2(t: Box<dyn Animal>) {
assert_eq!(t.get_age(), 10);
}
let dd = Dog { age: 10 };
static_invoke(dd);
let dd = Dog { age: 10 };
dynamic_invoke1(&dd);
dynamic_invoke2(Box::new(dd));
}
// 实验:rust对 &dyn Animal 和 Box<dyn Animal> 处理方式大体一致
// 区别:Box<dyn Animal>所有权就直接进了Vec
// 区别:&dyn Animal的所有权不属于Vec
#[test]
fn use_trait_object2() {
let mut my_pets: Vec<&dyn Animal> = vec![];
let pet1 = Tiger {
_name: String::from("猫山王"),
};
let pet2 = Box::new(Cat { _age: 0.32f32 });
let pet3 = Box::new(Cat { _age: 1.32f32 });
let pet4 = Dog { _is_happy: false };
my_pets.push(&pet1);
my_pets.push(pet2.as_ref());
my_pets.push(pet3.as_ref());
my_pets.push(&pet4);
for pet in my_pets.iter() {
pet.run(); // 动态分发,运行时查方法表
}
println!("{:p}", &my_pets); // 0x89c14fd778 // 栈上地址
println!("{:p}", &my_pets[0]); // 0x1bdbf7a94 80 +16 // 堆上地址
println!("{:p}", &my_pets[1]); // 0x1bdbf7a94 90 +16 // 堆上地址
println!("{:p}", &my_pets[2]); // 0x1bdbf7a94 a0 +16 // 堆上地址
println!("{:p}", &my_pets[3]); // 0x1bdbf7a94 b0 +16 // 堆上地址
}
// 重载运算符
#[test]
fn redef_oper1() {
struct Point {
x: i32,
y: i32,
}
// 为Point实现Add
// 重载 + 运算符
// 写法上,Self = Point
impl std::ops::Add for Point {
type Output = Point;
// 注意:这里是把self和other的所有权多拿走了
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: 2, y: 1 };
let p3 = p1 + p2;
assert_eq!(p3.x, 3);
assert_eq!(p3.y, 3);
}
// 完全限定语法
#[test]
fn use_full_function_name() {
trait Pilot {
fn fly(&self) -> i32;
}
trait Wizard {
fn fly(&self) -> i32;
}
struct Human;
impl Pilot for Human {
fn fly(&self) -> i32 {
1
}
}
impl Wizard for Human {
fn fly(&self) -> i32 {
0
}
}
impl Human {
fn fly(&self) -> i32 {
2
}
}
let h = Human {};
assert_eq!(Pilot::fly(&h), 1);
assert_eq!(Wizard::fly(&h), 0);
assert_eq!(h.fly(), 2);
}
// 完全限定语法
// 关联函数 与 trait 非self默认实现
#[test]
fn use_full_function_name2() {
trait Animal {
fn baby_name() -> String;
}
struct Dog;
impl Dog {
fn baby_name() -> String {
String::from("Spot")
}
}
impl Animal for Dog {
fn baby_name() -> String {
String::from("puppy")
}
}
assert_eq!(Dog::baby_name(), "Spot");
assert_eq!(<Dog as Animal>::baby_name(), "puppy");
}
// trait 继承
#[test]
fn trait_parent() {
trait HaveName {
fn get_name(&self) -> String;
}
// trait继承
trait SayHello: HaveName {
// 默认实现
fn say_hello(&self) {
println!("hello {}!", self.get_name());
}
}
// 如果结构体想要实现SayHello,就必须要实现其所有父级的所有接口方法
struct Dog;
impl SayHello for Dog {}
impl HaveName for Dog {
fn get_name(&self) -> String {
return String::from("doggy");
}
}
let dg = Dog {};
dg.say_hello();
}
// newtype 模式用以在外部类型上实现外部 trait
// 使用这个模式没有运行时性能惩罚
// 忽略孤儿原则
// Vec<T> 上实现 Display
#[test]
fn impl_display_for_vec() {
use std::fmt::{Display, Formatter, Result};
// 直接实现的话,不允许,编译报错:
// only traits defined in the current crate can be implemented for arbitrary types
// impl doesn't use only types from inside the current crate
// impl Display for Vec<u32> {}
// 重定义一个元组型结构体,内部就单个类型字段
struct Wrapper(Vec<String>);
impl Display for Wrapper {
fn fmt(&self, f: &mut Formatter) -> Result {
// 这里毕竟多套了一层,只能每次用 .0 来调用了
// 需要避免每个方法都封装一把的话,可实现 Deref trait,使其自动解引用
write!(f, "[{}]", self.0.join(", "))
}
}
}

最后

以上就是文艺铃铛为你收集整理的Rust语法、特性自学笔记的全部内容,希望文章能够帮你解决Rust语法、特性自学笔记所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(112)

评论列表共有 0 条评论

立即
投稿
返回
顶部