Rust系列(六)内置结构

Posted by YaPi on February 24, 2022

智能指针 Box

类似于C++中的智能指针。Box允许将一个值放在堆上而不是栈上,留在栈上的则是指向 堆数据的指针。当一个Box超出作用域时,它的析构函数被调用,内部对象被销毁,堆上的 内存被释放。

fn main(){
    let b = Box::new(5);
    println!("b = {}",b);
}

Box没有运行上的性能损失,即便如此也需要在恰当时候使用:

  • 当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型时
// 这种类型在编译的时候无法获取大小
enum List{
    //Cons(i32,List),
    // 修改为List地址类型
    Cons(i32,Box<List>),
    Nil
}

fn main(){
    let list = List::Cons(0,Box::new(List::Cons(1,Box::new(List::Cons(2,Box::new(List::Nil))))));
}
  • 当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候
fn main(){
    // a 数组元素很多个 0 都存储在栈上面
    let a = [0,1024 * 512];

    // 会发生拷贝,从栈上拷贝到堆上
    let a_box= Box::new(a);
}
  • 当希望拥有一个值并只关心它的类型是否实现了特定trait而不是其具体类型的时候
fn main()-> Result<(),Box<dyn std::error::Error>>{
    let f = std::fs::read("/tmp/t.txt");
    Ok(());
}

Rc 引用计数

你可以将Rc看作Box的高级版本:它是带引用计数的智能指针。只有当它的引用计数为0时,数据才会 被清理。

// 需要先引入 Rc
use std::rc::Rc;
use List::Cons;

enum List{
    //Cons(i32,List),
    // 修改为List地址类型
    Cons(i32,Rc<List>),
    Nil
}

fn main(){
    // Rc 使一个值可以有多个所有者
    let four = Rc::new(Cons(4,Rc::new(List::Nil)));
    // 调用clone方法后,引用计数会加1, 不是说将原来的数据复制一份儿
    let zero = List::Cons(0,Rc::new(List::Cons(1,four.clone())));
    // 使用 Rc::clone(&four)一样的可以复制
    let two = List::Cons(2,Rc::new(List::Cons(3,Rc::clone(&four))));
    let three = List::Cons(2,Rc::new(List::Cons(4,Rc::clone(&four))));
}

Vector

Vector是动态大小的数组,与切片一样,它们的大小在编译时是未知的,但它们可以随时增长或收缩。 有三个重要属性:

  • 指向数据的指针
  • 长度
  • 容量

容量表示为向量预留了多少内存,一旦长度大于容量,向量将申请更大的内存进行重新分配。

fn main(){
    // 使用宏创建
    // let v : Vec<i32> = vec![0,1,2,3];
    let mut v = Vec::new();
    for i in 0..10{
        v.push(i);
    }
    println!("{:?}",v);
    println!("v.len = {:?}",v.len());
    // 预留容量
    println!("v.capacity = {:?}",v.capacity());
    // 抛出最末尾数据
    println!("抛出末尾数据: {:?}",v.pop());

    // for i in 0..v.len(){
    //     println!("index = {}, value = {}",i,v[i])
    // }

    // for e in v.iter() {
    //     println!("{:?}",e)
    // }
    
    // 可修改元素的遍历
    for e in v.iter_mut() {
        *e *= 2;
    }
    
    // 遍历
    for e in v.iter() {
        println!("修改后 : {:?}",e)
    }

    // v.copy_from_slice()
}

HashMap

HashMap 是一种key-value 映射结构。可动态调整大小。大多数数据类型都可作为HashMap的key 只要其实现了Eq和Hash traits。

// 引入
use std::collections::HashMap;

fn main(){
    let mut m:HashMap<&str, u32> = HashMap::new();
    m.insert("tom",95);
    m.insert("bom",96);

    // 需要取地址符取key
    match m.get(&"tom") {
        Some(data) => println!("data = {}",data),
        None => println!("None")
    }
    // 删除
    m.remove(&"tom");
    // 需要取地址符取key
    match m.get(&"tom") {
        Some(data) => println!("data = {}",data),
        None => println!("None")
    }
    // 遍历是无序的
    for (name, score) in m.iter(){
        println!("{:?} {:?}",name,score);
    }
}

字符串类型 str, &str 与String的区别

fn main(){
    // "Hello World" 字面量 存储在二进制文件中,被保存在数据段的区域
    // str类型几乎不会被用到,总是会使用&str
    // &str 代表的是在内存中(数据段、代码段、堆、栈...)字符串数据
    // &str 可以引用数据段中的内容,也可以是堆里面的内容
    let s:&str = "Hello World";
    let ss:&'static str = "Hello World";
    // String类型它拥有自己的数据,可以修改
    // String 类型是存储在堆里面的
    let mut t:String = String::from(ss);
    t.push_str("!!");
    println!("{}",t);
    echo(s);
    echo(&t);
}

struct Foo {
    // 结构体内部一般使用String
    name: String,
}

// 函数参数定义字符串一般使用 &str这种格式
fn echo(s:&str){
    println!("{:?}",s);
}

SystemTime

use std::time::{SystemTime,Duration};
use std::thread::sleep;

// 若需要更完善的时间操作,可使用 chrono 库
fn main(){
    // 系统时间
    // 通过系统调用请求操作系统返回的系统时间
    let now = SystemTime::now();
    println!("{:?}",now);

    // timeStamp 是自从1970年1月1日到现在的秒数
    let timeStamp = now.duration_since(SystemTime::UNIX_EPOCH);
    println!("{:?}",timeStamp.unwrap());

    // sleep 5 s
    sleep(Duration::from_secs(5));
    // ela 程序休眠的时间
    println!("ela ={:?}", now.elapsed().unwrap());

    // 获取一分钟之后的时间
    let future = now.checked_add(Duration::from_secs(60));
    println!("future = {:?}",future);
}