rust智能指针、cargo
cargo、crates.io
release profile
通过release profile
(发布配置)来自定义构建
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
}
常用:Examples、Panics、Errors、Safety
cargo test的时候,文档注释的代码也会运行测试。
外层条目:
//! # cratetest
//!
//! `cratetest` is a ...
//!
/// Adds one to the number given.
/// ...
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);
}
cargo工作空间
一套共享同一个Cargo.lock
和输出文件夹的包。
帮助管理多个相互管理且需要协同开发的crate
。
工作空间只有一个Cargo.lock文件,在顶层目录
- 保证工作空间内所有crate使用的依赖版本相同
- 工作空间内crate相互兼容
cargo install
安装二进制crate
只能安装具有二进制目标的crate。
安装位置在根目录.cargo/bin/
目录下。
智能指针
- 行为和指针相似,但有额外的数据。
通常使用struct实现,实现了
- Deref trait:允许智能指针struct的实例像引用一样使用
- Drop trait: 允许自定义智能指针实例走出作用域的代码
Box<T>
在heap上分配值。
Box<T>
是最简单的智能指针,允许在heap上存储数据,没有性能开销,也没有其他功能。
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
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
两个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正常的可变性和借用规则。
借用规则:
任意时间,只能拥有一个可变引用;或者只能有多个不可变应用。
内部可变性:可变的借用一个不可变的值。
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智能指针部分