Rust语法、特性自学笔记

这篇博客详细记录了作者自学Rust编程语言的过程,涵盖了Rust的基础语法、所有权系统、类型系统、函数与闭包、模块系统等关键概念。通过阅读,读者可以深入了解Rust语言的独特特性和高效编程技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



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 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值