rust智能指针、cargo


cargo、crates.io

release profile

通过release profile (发布配置)来自定义构建

image-20221130135032313

Cargo.toml

[package]
name = "mingrep"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

[profile.dev]
opt-level = 1
# 编译优化等级

# cargo build
# Finished dev [optimized + debuginfo] target(s) in 1.55s

[profile.release]
opt-level = 3

crates.io:可以通过发布包共享代码。

文档注释

  • 生成HTML文档,如何使用API。///
  • cargo doc
  • 文件在target/doc目录下
/// Adds one to the number given.
/// 
/// # Examples
/// 
/// ```
/// let arg = 5;
/// let answer = mycrate::add_one(arg);
/// 
/// assert_eq!(6,answer);
/// ```
/// 
pub fn add_one(x: i32) -> i32 {
    x + 1
}

image-20221130141229538

常用:Examples、Panics、Errors、Safety

cargo test的时候,文档注释的代码也会运行测试。

外层条目

//! # cratetest
//! 
//! `cratetest` is a ...
//! 

/// Adds one to the number given.
/// ...

image-20221130141904684

pub use

导出方便使用的公共API

  • 开发者会把程序分成很多层,但是外部使用者使用时比较费劲。

如原来:

// lib.rs
pub mod utils {
    /// add 2 to given number
    pub fn add_two(x: i32) -> i32 {
        x + 2
    }
}

// main.rs
use cratetest::utils::add_two;

fn main() {
    let a = 3;
    let b = add_two(a);
    println!("{}",b);
}

层次比较深,开发方便,但是对于使用者不方便。

pub use后:

// lib.rs
pub use self::utils::add_two;

// main.rs
use cratetest::add_two;

fn main() {
    let a = 3;
    let b = add_two(a);
    println!("{}",b);
}

image-20221130143001671

cargo工作空间

一套共享同一个Cargo.lock和输出文件夹的包。

帮助管理多个相互管理且需要协同开发的crate

工作空间只有一个Cargo.lock文件,在顶层目录

  • 保证工作空间内所有crate使用的依赖版本相同
  • 工作空间内crate相互兼容

image-20221130152644511

cargo install

安装二进制crate

只能安装具有二进制目标的crate。

安装位置在根目录.cargo/bin/目录下。

智能指针

  • 行为和指针相似,但有额外的数据。

通常使用struct实现,实现了

  • Deref trait:允许智能指针struct的实例像引用一样使用
  • Drop trait: 允许自定义智能指针实例走出作用域的代码

Box<T>

在heap上分配值。

Box<T>是最简单的智能指针,允许在heap上存储数据,没有性能开销,也没有其他功能。

image-20221130163101106

fn main() {
    let b = Box::new(5);

    println!("b={}",b);
}

常用场景

  • 某类型的大小无法确定,但是使用时上下文需要知道确切大小。

  • 大量数据移交所有权,确保不会被复制

Box处理递归类型

fn main() {
    let list = Cons(1,
        Box::new(Cons(2,
            Box::new(Cons(3,
                Box::new(Nil))))));
    
}

enum List {
    Cons(i32, Box<List>),
    Nil,
}

Deref Trait

解引用


struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }  
}

impl<T> Deref for MyBox<T> {
    // 可以使用type关键字声明另一类型的别名
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

fn main() {
    let x = 5;
    let y = &x;
    let z = MyBox::new(x);

    assert_eq!(5, x);
    assert_eq!(*y, 5);
    assert_eq!(*z, 5);
}

解引用转化

当类型的引用传递给函数时,但是参数类型不匹配:

  • 解引用转化会自动发生
  • deref

image-20221201120554224

Drop Trait

离开作用域时发生的动作。

struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    // drop 不能显式调用, 但是可以用std::mem::drop函数来手动调用
    fn drop(&mut self) {
        println!("dropping Mybox with data `{}`!",self.data);
    }
}

fn main() {
    let a = CustomSmartPointer {data: "hi".to_string()};
    let b = CustomSmartPointer {data: "he".to_string()};
}
// dropping Mybox with data `he`!
// dropping Mybox with data `hi`!

Rc<T>

引用计数智能指针。

使用场景

需要在heap上分配数据,被多个部分读取(只读),但是编译时无法确定哪个部分最后用完这些数据。

只能用于单线程场景。

demo

image-20221201122435467

两个List共享另一个List的所有权。

enum List {
    // cons是 construct 的简写Lisp 程序员的意思是分配内存。
    // 分配大量内存的程序被称为 cons太多了。
    Cons(i32, Rc<List>),
    Nil,
}

use std::rc::Rc;

use crate::List::{Cons,Nil};

fn main() {
    let a = Rc::new(Cons(5, 
        Rc::new(Cons(10,
            Rc::new(Nil)))));

    println!("强引用个数:{}", Rc::strong_count(&a));

    {
        // Rc::clone增加引用,不会拷贝数据
        let b = Cons(3, Rc::clone(&a));
        println!("强引用个数:{}", Rc::strong_count(&a));
    }

    let c = Cons(4, Rc::clone(&a));
    println!("强引用个数:{}", Rc::strong_count(&a));
}

// 强引用个数:1
// 强引用个数:2
// 强引用个数:2

RefCell<T>和内部可变性

RefCell持有了数据的唯一所有权。

也是只能用于单线程场景。

允许在只持有不可变引用的前提下对数据修改。

  • 数据结构中使用了unsafe代码来绕过Rust正常的可变性和借用规则。

借用规则:

任意时间,只能拥有一个可变引用;或者只能有多个不可变应用。

image-20221201124423491

image-20221201124537362

内部可变性:可变的借用一个不可变的值。

image-20221201154909669

use std::{rc::Rc, cell::RefCell};
use crate::List::{Nil, Cons};

#[derive(Debug)]
enum List {
    Cons(Rc<RefCell<i32>>, Rc<List>),
    Nil,
}


fn main() {
    let value = Rc::new(RefCell::new(5));
    let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
    let b = Cons(Rc::new(RefCell::new(6)), Rc::clone(&a));
    let c = Cons(Rc::new(RefCell::new(10)), Rc::clone(&a));
    
    println!("a: {:?}",a); // 5, nil
    println!("b: {:?}",b); // 6, 5, nil
    println!("c: {:?}",c); //10, 5, nil

    *value.borrow_mut() += 10;

    println!("a: {:?}",a); // 15, nil
    println!("b: {:?}",b); // 6, 15, nil
    println!("c: {:?}",c); //10, 15, nil
}

循环引用导致内存泄露

一些引用表达所有权,一些引用不表达所有权。

Rc<T>换成Weak<T>之类。

这部分比较复杂,指来指去的,强弱指针还不够,还可变不可变,得专门搞下。

// TODO:rust智能指针部分