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
Rust语法、特性自学笔记
最新推荐文章于 2025-04-10 07:15:00 发布