【Rust5】常用crates



2022年09月17日    Author:Guofei

文章归类: 合集    文章编号: 11205

版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2022/09/17/rust5.html


rand随机

rand = "0.8.3"

use rand::Rng;


// 每个线程都有一个初始化的生成器
let mut rng = rand::thread_rng();

// 整数范围是该类型的范围
let n1: u8 = rng.gen();
let n2: u16 = rng.gen();
// 浮点数的范围是0到1,但不包括1
let f1: f64 = rng.gen();

生成一个范围内的随机数

let f2_rnd: f64 = rng.gen_range(0.0..10.0);
let i2_rnd: i32 = rng.gen_range(0..10);

指定分布

// 整数
Uniform::from(1..7);
// 浮点数
Uniform::from(1.0..7.0);


// 更复杂的类型用 rand_distr
use rand_distr::{Normal, Binomial, Gamma};

// mean 2, standard deviation 3
let mut normal = Normal::new(2.0, 3.0).unwrap();
let val = normal.sample(&mut rand::thread_rng());

案例:随机生成密码

use rand::Rng;
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                        abcdefghijklmnopqrstuvwxyz\
                        0123456789)(*&^%$#@!~";
const PASSWORD_LEN: usize = 30;
let mut rng = rand::thread_rng();

let password: String = (0..PASSWORD_LEN)
    .map(|_| {
        let idx = rng.gen_range(0..CHARSET.len());
        CHARSET[idx] as char
    })
    .collect();

sort

vec.sort 可以用于 int, &str 但不能用于 float

let mut vec = vec![1, 5, 10, 2, 15];
let mut vec = vec!["aa", "ab", "b"];

vec.sort();
print!("{:?}", vec);

下面这种写法很通用

let mut vec = vec![1.1, 1.15, 5.5, 1.123, 2.0];
vec.sort_by(|a, b| a.partial_cmp(b).unwrap());

案例:对 vec<struct> 排序

struct Person {
    name: String,
    age: u32,
}

impl Person {
    fn new(name: String, age: u32) -> Self {
        Person { name, age }
    }
}

fn main() {
    let mut people = vec![
        Person::new("Zoe".to_string(), 25),
        Person::new("Al".to_string(), 60),
        Person::new("John".to_string(), 1),
    ];

    people.sort_by(|a, b| b.age.cmp(&a.age));
}

terminal相关

命令行工具 clap

// clap = "*"
use clap::{Arg, App};

fn main() {
    let matches = App::new("app名称")
        .version("0.0.1")
        .author("guofei9987 <me@guofei.site>")
        .about("关于,这个app是干什么的")
        .arg(Arg::with_name("file")
            .short('f')
            .long("file")
            .takes_value(true)
            .help("A cool file"))
        .arg(Arg::with_name("num")
            .short('n')
            .long("number")
            .takes_value(true)
            .help("Five less than your favorite number"))
        .get_matches();

    let myfile = matches.value_of("file").unwrap_or("input.txt");
    println!("The file passed is: {}", myfile);

    let num_str = matches.value_of("num");
    match num_str {
        None => println!("No idea what your favorite number is."),
        Some(s) => {
            match s.parse::<i32>() {
                Ok(n) => println!("Your favorite number must be {}.", n + 5),
                Err(_) => println!("That's not a number! {}", s),
            }
        }
    }
}

使用

# 正经使用
cargo run -- -f myfile.txt -n 251

# -V 和 --version 显示app名称+版本
cargo run -- -V

# -h, --help 显示详细的帮助信息
cargo run -- -h

ANSI:彩色命令行

// ansi_term = "*"
use ansi_term::Colour;
use ansi_term::Style;

fn main() {
    println!("This is {}, {}, {} and {}. This is {}.",
             Colour::Red.paint("red"),
             Colour::Blue.paint("blue"),
             Colour::Green.paint("green"),
             Colour::Yellow.paint("yellow"),
             Style::new().bold().paint("bold"),
    );
}

ratatui:命令行交互界面

tui = { package = "ratatui", version = "0.26.0", features = ["crossterm"], default_features = false }
crossterm = "0.27.0"
anyhow = "1.0.81"

画 line 图

use anyhow::Result;
use crossterm::{
    event::{self, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::{io, vec};
use tui::{
    backend::{Backend, CrosstermBackend},
    layout::{Constraint, Direction, Layout},
    Terminal,
    widgets::{Axis, Block, Borders, Chart, Dataset},
    symbols::Marker, // 正确导入 Marker
    style::{Color, Style},
};
use std::io::BufWriter;

fn main() -> Result<()> {
    // 准备数据
    let x: Vec<f64> = (0..630).map(|x| x as f64 * 0.01).collect();
    let data: Vec<(f64, f64)> = x.iter().map(|x| (*x, x.sin())).collect();
    let data2: Vec<(f64, f64)> = x.iter().map(|x| (*x, x.cos())).collect();

    // 设置终端
    enable_raw_mode()?;
    let stdout = io::stdout();
    let backend = CrosstermBackend::new(BufWriter::new(stdout));
    let mut terminal = Terminal::new(backend)?;
    execute!(terminal.backend_mut(), EnterAlternateScreen)?;

    // 设置 x_axis
    let x_axis_range: [f64; 2] = [0.0, 6.3];
    let x_axis_num = 3;
    let x_step = (x_axis_range[1] - x_axis_range[0]) / x_axis_num as f64;
    let x_labels = (0..=x_axis_num).map(|i| tui::text::Span::styled(format!("{:.1}", x_axis_range[0] + x_step * i as f64), Style::default().add_modifier(tui::style::Modifier::BOLD))).collect();

    // 设置 y_axis
    let y_axis_range: [f64; 2] = [-1.0, 1.0];
    let y_axis_num = 5;
    let y_step = (y_axis_range[1] - y_axis_range[0]) / y_axis_num as f64;
    let y_labels = (0..=y_axis_num).map(|i| tui::text::Span::styled(format!("{:.1}", y_axis_range[0] + y_step * i as f64), Style::default().add_modifier(tui::style::Modifier::BOLD))).collect();


    terminal.draw(|f| {
        let size = f.size();
        let chart = Chart::new(
            vec![
                Dataset::default()
                    .name("Data")
                    .marker(Marker::Braille)
                    .style(Style::default().fg(Color::Magenta))
                    .data(&data),
                Dataset::default()
                    .name("Data2")
                    .marker(Marker::Braille)
                    .style(Style::default().fg(Color::Red))
                    .style(Style::default().bg(Color::Blue))
                    .data(&data2),
            ]
        )
            .block(Block::default().title("Line Chart").borders(Borders::ALL))
            .x_axis(Axis::default()
                .title("X Axis")
                .style(Style::default().fg(Color::Gray))
                .bounds(x_axis_range)
                .labels(x_labels))
            .y_axis(Axis::default()
                .title("Y Axis")
                .style(Style::default().fg(Color::Gray))
                .bounds(y_axis_range)
                .labels(y_labels));
        f.render_widget(chart, size);
    })?;


    // 等待用户按键退出
    loop {
        if let CEvent::Key(key) = event::read()? {
            if key.code == KeyCode::Char('q') || key.code == KeyCode::Esc {
                break;
            }
        }
    }

    // 清理和退出
    disable_raw_mode()?;
    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
    terminal.show_cursor()?;


    Ok(())
}

画bar

use anyhow::Result;
use crossterm::{
    event::{self, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::io;
use tui::{
    backend::{Backend, CrosstermBackend},
    layout::{Constraint, Direction, Layout},
    Terminal,
    widgets::{BarChart, Block, Borders},
    style::{Color, Style},
};
use std::io::BufWriter;

fn create_bar_chart<B: Backend>(terminal: &mut Terminal<B>, data: Vec<(&str, u64)>) -> Result<()> {
    terminal.draw(|f| {
        let size = f.size();
        let bar_chart = BarChart::default()
            .block(Block::default().title("Bar Chart").borders(Borders::ALL))
            .bar_style(Style::default().fg(Color::Yellow))
            .data(&data)
            .bar_width(3)
            .bar_gap(2)
            .value_style(Style::default().fg(Color::Black).bg(Color::Yellow));
        f.render_widget(bar_chart, size);
    })?;
    Ok(())
}

fn main() -> Result<()> {
    // 初始化数据
    let data = vec![
        ("A", 3),
        ("B", 7),
        ("C", 2),
        ("D", 5),
    ];
    // 设置终端
    enable_raw_mode()?;
    let stdout = io::stdout();
    let backend = CrosstermBackend::new(BufWriter::new(stdout));
    let mut terminal = Terminal::new(backend)?;
    execute!(terminal.backend_mut(), EnterAlternateScreen)?;
    // 绘制条形图
    create_bar_chart(&mut terminal, data)?;
    // 等待用户按键退出
    loop {
        if let CEvent::Key(key) = event::read()? {
            if key.code == KeyCode::Char('q') || key.code == KeyCode::Esc {
                break;
            }
        }
    }
    // 清理和退出
    disable_raw_mode()?;
    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
    terminal.show_cursor()?;
    Ok(())
}

二维码相关

image = "0.23.14"
qrcode = "0.12.0"
quircs = "*"

解释

  • image 用来处理图片
  • qrcode 用来生成二维码
    • 可以生成格式为 png、svg、字符
    • 支持二维码协议中的全部格式,包括 Micro QR
  • quircs 用来读取二维码
    • 经测试,支持拍屏

参数

  • EcLevel:L、M、Q、H
  • Version::Micro 1 到 4
  • Version::Normal 1 到 40
version EcLevel 长度 二维码大小 备注
Version::Micro(1) 只能是 L 字符最长5,不能用比特 11x11  
Version::Micro(2) L 字符最长10,比特最长6 13x13  
Version::Micro(2) M 字符最长8,比特最长6 13x13  
Version::Micro(3)     15x15  
Version::Micro(4)     17x17  
Version::Normal(1)     21x21  
。。。        
Version::Normal(40) 都可以      
Version::Normal(40) L 数字7089,字母数字 4296,字节 2954,汉字1817 177x177,周围要留出4的空白,实际占用 185x185  

这是因为,QR码有这些编码模式

  1. 数字模式:用于编码0-9的纯数字
  2. 字母数字模式:0-9a-zA-Z
  3. 字节模式
  4. 日本汉字模式

纠错级别:

  • L(Low): 7% 的错误恢复能力
  • M(Medium): 15% 的错误恢复能力
  • Q(Quartile): 25% 的错误恢复能力
  • H(High): 30% 的错误恢复能力

quircs:二维码识别

  • micro 的识别不出来(opencv也不能)

time

时间差

let start = Instant::now();
thread::sleep(Duration::from_secs(1));
let duration = start.elapsed();
// duration 可以转化为很多种数字格式
// duration.as_secs_f32(); // 返回 f32,

now

// std::time::Duration 重复了,所以做个别名
use chrono::Duration as Duration2;

let now = Utc::now();
now.checked_add_signed(Duration2::days(-1))
// 还可以用checked_sub_signed
// Duration2:: 可以是:
// days, weeks,
// hours, minutes, milliseconds, microseconds

时区(略)

生成时间对象

let now = Utc::now();

let date_time = chrono::NaiveDate::from_ymd(2017, 11, 12).and_hms(17, 33, 44);

时间对象格式转字符串

let now = Utc::now();

// 标准格式时间
let time_str = format!("{}-{:02}-{:02} {:02}:{:02}:{:02}",
                       now.year(), now.month(), now.day(),
                       now.hour(), now.minute(), now.second());



// 其它格式时间
let (is_common_era, year) = now.year_ce();
println!(
   "The current UTC date is {}-{:02}-{:02} {:?} ({})",
   year,
   now.month(),
   now.day(),
   now.weekday(),
   if is_common_era { "CE" } else { "BCE" }
);

let (is_pm, hour) = now.hour12();
println!(
   "The current UTC time is {:02}:{:02}:{:02} {}",
   hour,
   now.minute(),
   now.second(),
   if is_pm { "PM" } else { "AM" }
);

时间对象转 timestamp

now.timestamp()

字符串转日期

let no_timezone = chrono::NaiveDateTime::parse_from_str("2015-09-05 23:56:04", "%Y-%m-%d %H:%M:%S")
    .unwrap();

sleep

use std::thread;
use std::time::Duration

thread::sleep(Duration::from_secs(2));

正则

[dependencies]
regex = "*"
// 编译正则
let re = Regex::new("\\b[\\w\\-\\+\\.]+@[\\w\\-\\+\\.]+\\.\\w{2,18}\\b").unwrap();
let text = "aaa  me@guofei.site guofei9987@foxmail.com";


// 是否有匹配,
let res: bool = re.is_match(text);
// true


// 单匹配
let mut res = Vec::new();
match re.captures(text) {
  // 这里面的序号0代表整体,序号1代表分组1
    Some(i) => { res.push(String::from(&i[0])) }
    None => {}
}




// 多匹配
let mut res = Vec::new();
for cap in re.captures_iter(text) {
    res.push(String::from(&cap[0]));
}


// 匹配并替换
let text_new = re.replace_all(text, "你");

关于分组运算

// 编译正则,定义老公两个组,分别命名为 user_name 和 domain
let re = Regex::new("\\b(?P<name>[\\w\\-\\+\\.]+)@[\\w\\-\\+\\.]+\\.(?P<domain>\\w{2,18})\\b").unwrap();
let text = "aaa  me@guofei.site guofei9987@foxmail.com";
let text2 = "aaaaaaa";


// 单匹配:上面用 index 方便一些,如果想用name:
// re.captures(text) 是一个 `<Option<Captures<"user_name":Option>>>` 这种嵌套
let mut res: Vec<String> = Vec::new();
match re.captures(text) {
    Some(i) => {
        res.push(String::from(i.name("user_name").unwrap().as_str()));
    }
    None => {}
}

// 还可以这样,但是匹配不到的时候直接 Panic 了
let res: &str = cap.unwrap().name("user_name").unwrap().as_str();


// 多匹配
let mut res = Vec::new();
for cap in re.captures_iter(&text) {
    match cap.name("user_name") {
        None => {}
        Some(i) => { res.push(String::from(i.as_str())) }
    }
}


// 替换
let text_new = re.replace_all(text, "$user_name/$domain");

正则语法

.             # 除新行以外的任何字符(包括带有s标志的新行)
\d            # 数字 (\p{Nd})
\D            # 非数字

\pN           # 单字母名称Unicode字符类
\PN           # Negated(非) 单字母名称Unicode字符类

\p{Greek}     # Unicode字符类(常规类别或脚本)
\P{Greek}     # negated(非) Unicode字符类(常规类别或脚本)
[xyz]         # 匹配x、y或z(并集)的字符类。
[^xyz]        # 匹配 除x、y和z以外的 任何字符的字符类。
[a-z]         # 匹配A-z范围内任何字符的字符类。
[[:alpha:]]   # ASCII 字符类 ([A-Za-z])
[[:^alpha:]]  # Negated(非) ASCII 字符类 ([^A-Za-z])
[x[^xyz]]     # ???嵌套/分组字符类(匹配除y和z以外的任何字符)
[a-y&&xyz]    # 交集(匹配x或y)
[0-9&&[^4]]   # 使用交集和求反的减法(匹配0-9,4除外)
[0-9--4]      # 直接减法            (匹配0-9,4除外)
[a-g~~b-h]    # 对称差异(范围异或)(仅匹配“a”和“h”)
[\[\]]        # 字符类中的转义(匹配[ 或 ])
[[:alnum:]]        # alphanumeric ([0-9A-Za-z])
[[:alpha:]]        # alphabetic ([A-Za-z])
[[:ascii:]]        # ASCII ([\x00-\x7F])
[[:blank:]]        # blank ([\t ])
[[:cntrl:]]        # control ([\x00-\x1F\x7F])
[[:digit:]]        # digits ([0-9])
[[:graph:]]        # graphical ([!-~])
[[:lower:]]        # lower case ([a-z])
[[:print:]]        # printable ([ -~])             可打印
[[:punct:]]        # punctuation ([!-/:-@\[-`{-~]) 标点符号,不包含汉字标点
[[:space:]]        # whitespace ([\t\n\v\f\r ])    空白
[[:upper:]]        # upper case ([A-Z])
[[:word:]]         # word characters ([0-9A-Za-z_])
[[:xdigit:]]       # hex digit ([0-9A-Fa-f])       十六进制数字

参考:https://blog.csdn.net/wsp_1138886114/article/details/116519242 (rust的正则和python的正则有些区别,功能更多)

IO

include_str&include_bytes

include_str!include_bytes! 它在编译时将文件的内容作为字符串直接嵌入到代码中。

  • 优点
    • 无需运行时文件IO,从而减少开销
    • 简化部署: 文件就在编译后的文件中了。1)只需分发单个可执行文件。 2)无需担心路径和位置问题。 3)从而规避文件不存在、文件权限问题带来的 bug
    • 提高应用安全性:文件内容是嵌入在代码中的,不能被随意访问、修改
  • 缺点
    • 如果文件很大:会增加编译时间、增加内存消耗、增加程序启动时间

返回的类型是 &str, &[u8; ?]

std::fs 读取和写入

参考:

https://llever.com/rust-cookbook-zh/file/read-write.zh.html

https://blog.csdn.net/feiyanaffection/article/details/125575331?spm=1001.2014.3001.5502

读文件

let content = "hello, world!";
let filename = "output.txt";

// 最简易的方法

// 读
let data_bytes: Vec<u8> = fs::read(filename)
    .map_err(|e| e.to_string())?;
// 写
fs::write(filename2, data_bytes)
    .map_err(|e| e.to_string())?;

// 如果是文本文件,还可以直接读到 String 
let data_string: String = fs::read_to_string("file.txt")
    .map_err(|e| e.to_string())?;


// 使用 buffer。好处:可以预先分配内存,性能可能更高些。
let mut file = fs::File::open(filename)
    .map_err(|e| e.to_string())?;
let mut buf = vec![0; 100]; // 一次读入的数量,不会比它大
// read_num: 这次读入的字节数
let read_num = file.read(&mut buf).
    map_err(|e| e.to_string())?;

// 使用 buffer 读到 String
let mut file = fs::File::open(filename)
    .map_err(|e| e.to_string())?;
let mut buf = String::new();
file.read_to_string(&mut buf)
    .map_err(|e| e.to_string())?;




// 逐行读取
let mut file = std::fs::File::open("new_file.txt").unwrap();
let read = BufReader::new(file);
for line in read.lines() {
    let line = line.unwrap();
    println!("{}", line);
}

// 追加写
let mut file = std::fs::OpenOptions::new()
    .write(true) // 写入
    .create(true)// 或者 .create_new(true) :如果文件存在则报错
    .append(true) // true 是追加写,false 是覆盖写
    .open(filename).unwrap();
let n = file.write(content.as_bytes()).unwrap(); // 实际写入的字节数

目录

use std::{env, fs};

fn main() {
    let current_dir = env::current_dir().unwrap();
    println!(
        "Entries modified in the last 24 hours in {:?}:",
        current_dir
    );

    for entry in fs::read_dir(current_dir).unwrap() {
        let entry = entry.unwrap();
        let path = entry.path();

        let metadata = fs::metadata(&path).unwrap();
        let last_modified = metadata.modified().unwrap().elapsed().unwrap().as_secs();

        if last_modified < 24 * 3600 && metadata.is_file() {
            println!(
                "Last modified: {:?} seconds, is read only: {:?}, size: {:?} bytes, filename: {:?}",
                last_modified,
                metadata.permissions().readonly(),
                metadata.len(),
                path.file_name().ok_or("No filename").unwrap()
            );
        }
    }
}

调用 cmd

use std::process::{Command, Output};

fn main() {
    let res = Command::new("ls").arg("-l").output();

    match res {
        Ok(output) => {
            println!("{}", output.status.success());
            println!("{}", String::from_utf8(output.stdout).unwrap());
        }
        Err(_) => { println!("command 出错"); }
    }
}

更多:https://llever.com/rust-cookbook-zh/os/external.zh.html

zip

下面的代码:

  • 先读取文件到字节流
  • 然后用 zip 读取字节流,若干处理后生成一个新的字节流,
  • 然后把字节流保存到文件
let filename = "./files/file.zip";
let filename_new = "./files/file_new.zip";

// 读取文件到字节流
let data_bytes = fs::read(filename).unwrap();
// 从字节流读取 zip 文件
let cursor = Cursor::new(data_bytes);
let mut archive = zip::ZipArchive::new(cursor).unwrap();

// 新文件
let mut data_bytes_new: Vec<u8> = Vec::new();
let mut cursor_new = Cursor::new(&mut data_bytes_new);
let mut zip_writer = zip::ZipWriter::new(&mut cursor_new);

for i in 0..archive.len() {
    let mut file = archive.by_index(i).unwrap();
    let filename: String = file.name().to_string();

    let options = zip::write::FileOptions::default()
        .compression_method(file.compression()) // 还可以是别的值,例如 zip::CompressionMethod::Stored
        .unix_permissions(file.unix_mode().unwrap_or(0o755));

    if file.is_file() {
        // 读原始文件
        let mut buffer: Vec<u8> = Vec::new();
        file.read_to_end(&mut buffer).unwrap();

        // 新建一个文件
        zip_writer.start_file(filename, options).unwrap();
        // 写入
        zip_writer.write_all(&buffer).unwrap();
    } else if file.is_dir() {
        //     创建目录
        zip_writer.add_directory(filename, options).unwrap();
    }
}
zip_writer.finish().unwrap();

drop(zip_writer); // 释放 zip_writer,从而释放 cursor_new 的借用状态
// 重置 cursor_new 以获取完整的 ZIP 字节流
cursor_new.seek(SeekFrom::Start(0)).unwrap();

// 得到字节流
let data_bytes_new = cursor_new.into_inner();

// 写入
fs::write(filename_new, data_bytes_new).unwrap();

  • unix(包括Linux、macOS、IOS、Android) 的文件系统使用的是正斜线,windows 使用反斜线
  • 一般情况下,为了保证跨平台兼容性,都是使用正斜线。但偶尔有人不遵守这个约定
  • let filename: String = file.mangled_name().as_path().display().to_string(); 会根据当前操作系统来调整这个斜线
  • let filename: String = file.name().to_string(); 不会调整这个斜线(原始 zip 文件中是什么,这里就是什么)

下面的代码:读取一个 zip 文件,然后遍历它,把每个文件的内容都放到新的 zip 文件中

// zip = "*"

let filename = "files/file.xlsx";
let filename_new = "files/file_new.xlsx";
// 读取
let zip_file = fs::File::open(filename).unwrap();
let mut archive = zip::ZipArchive::new(zip_file).unwrap();

// 写入
let zip_file_new = fs::OpenOptions::new()
    .create(true).write(true).truncate(true)
    .open(filename_new).unwrap();
let mut zip_writer = zip::ZipWriter::new(zip_file_new);


for i in 0..archive.len() {
    let mut file = archive.by_index(i).unwrap();
    let filename = file.mangled_name();
    let options = zip::write::FileOptions::default()
        .compression_method(zip::CompressionMethod::Stored)
        .unix_permissions(file.unix_mode().unwrap_or(0o755));

    if file.is_file() {
        // 读
        let mut buffer: Vec<u8> = Vec::new();
        file.read_to_end(&mut buffer).unwrap();

        // 新建一个文件
        zip_writer.start_file(filename.as_path().display().to_string(), options).unwrap();
        // 写入
        zip_writer.write_all(&buffer).unwrap();
    } else if file.is_dir() {
        //     创建目录
        zip_writer.add_directory(filename.as_path().display().to_string(), options).unwrap();
    }
}

// 结束并关闭
zip_writer.finish().unwrap();

csv(待补充)

https://llever.com/rust-cookbook-zh/encoding/csv.zh.html

Json

说明

  • 可以把 struct 保存为 json
  • 配合 include_str! 可以编译时把文件内容放入内存

如何把一个 struct 保存为 json

serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct Person {
    name: String,
    age: usize,
}
let p1 = Person {
    name: "张三".to_string(),
    age: 10,
};

// 序列化
let serialized:String = serde_json::to_string(&p1).unwrap();

// 反序列化
let p_read: Person = serde_json::from_str(&serialized).unwrap();

https://llever.com/rust-cookbook-zh/encoding/complex.zh.html

tar(待补充)

https://rust-lang-nursery.github.io/rust-cookbook/compression/tar.html

科学计算

常量


ndarray

ndarray 支持多维数组、广播计算、矩阵计算

ndarray-linalg 提供了线性代数计算,如奇异值分解(SVD)、特征值分解(EVD)、QR 分解等。它利用底层的 BLAS(基础线性代数子程序)和 LAPACK(线性代数包)库来提升性能。

  • 依赖 OpenBLASOpenBLAS 是 C 和汇编语言写的高性能计算库。它适配各种操作系统和处理器,还能自动根据指令集来优化。NumpySciPy 也是基于 OpenBLAS
  • 需要先安装 OpenBLAS: brew install openblas ,还需要先配置环境 (麻烦)
use ndarray::Array;

fn main() {
    let mut a = Array::from_vec(vec![1., 2., 3., 4., 5.]);
    let mut b = Array::from_vec(vec![5., 4., 3., 2., 1.]);
    // let z = a + b;// 最好不要这样写, a 被 borrow 了

    // 改元素值
    a[1] = 10.;

    // 相加
    let w = &a + &b;


    println!("w = {}", w);
    println!("w.sum() = {}", w.sum());
    // println!()

    // a.append()
    // a.push();
    // a.push_row();
    // a.push_column();
    //
    // a.map();

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

    for elem in a.iter() {
        println!("{}", elem);
    }
}

多维矩阵

use ndarray::arr2;

fn main() {
    let a = arr2(&[[1, 2, 3],
        [4, 5, 6]]);

    let b = arr2(&[[6, 5, 4],
        [3, 2, 1]]);

    println!("矩阵加: {}", &a + &b);
    println!("矩阵加标量: {}", &a + 3);


    let a = arr2(&[[1, 2, 3],
        [4, 5, 6]]);

    let b = arr2(&[[6, 3],
        [5, 2],
        [4, 1]]);

    println!("矩阵积: {}", a.dot(&b));

    println!("标量积: {}", 5 * &a);
}

三角函数

let angle: f64 = 3.14 / 6.0; //弧度
let sin_angle: f64 = angle.sin();

println!("sin: {}, acos = {}, atan = {}", sin_angle, angle.acos(), angle.atan());

// 转度数/弧度
// angle.to_radians()
//  angle.to_degrees()

复数

// num = "*"
let complex_integer = num::complex::Complex::new(10, 20);
let complex_float = num::complex::Complex::new(10.1, 20.1);

println!("Complex integer: {}", complex_integer);
println!("Complex float: {}", complex_float);

nalgebra

nalgebra = "*"

生成动态大小的矩阵

// 生成一个 2x3 的矩阵,其元素全部为 1.0
let mut m1 = DMatrix::from_element(2, 3, 1.0);

let m2 = DMatrix::<f64>::from_row_slice(
    3, 3, &[1.0, 2.0, 3.0,
        4.0, 5.0, 6.0,
        7.0, 8.0, 9.0]);


// 这个是按列填充的,与 m2 不同(坑)
let vec1 = vec![1.0, 2.0, 3.0,
                4.0, 5.0, 6.0,
                7.0, 8.0, 9.0];
let m3 = DMatrix::from_vec(3, 3, vec1);

生成静态大小的矩阵(性能更高)

let m1 = matrix![1.0, 2.0, 3.0;
                        4.0, 5.0, 6.0;
                        7.0, 8.0, 9.0];


let m2 = Matrix3::new(
    1.0, 2.0, 3.0,
    4.0, 5.0, 6.0,
    7.0, 8.0, 9.0,
);


let m3 = Vector3::new(1.0, 2.0, 3.0);

计算

// 矩阵积
m1 * m2

// 对应元素积
m1.component_mul(m2)

// 转秩
m1.transpose()

// 行数和列数
m1.nrows()
m1.ncols()

转格式

// 转换元素的数据类型
m1.cast::<f64>()



let m1_static = OMatrix::<f32, U2, U2>::new(
    1.0, 2.0,
    3.0, 4.0,
);
let m1_dyn = DMatrix::from_element(2, 2, 1.0);

// 静态大小转动态大小
let m2_dyn = DMatrix::from_row_slice(4, 4, m1_static.as_slice()).transpose();
// 动态大小转静态大小
let m2_u4 = Matrix4::from_row_slice(m1_dyn.as_slice()).transpose();

SVD:

use nalgebra::{Matrix4};
 // 定义一个 4x4 的矩阵
let a = Matrix4::new(
    1.0, 2.0, 3.0, 4.0,
    5.0, 6.0, 7.0, 8.0,
    9.0, 10.0, 11.0, 12.0,
    13.0, 14.0, 15.0, 16.0,
);


// 执行 SVD
let svd = a.svd(true, true);

let u = svd.u.unwrap();
let sigma = svd.singular_values;
let vt = svd.v_t.unwrap();

println!("U 矩阵:\n{}", u);
println!("奇异值 Σ:\n{}", sigma);
println!("V^T 矩阵:\n{}", vt);


// 验证重建: A = U * Σ * V^T
let sigma_matrix = Matrix4::from_diagonal(&sigma);
let reconstructed = u * sigma_matrix * vt;
println!("重建后的矩阵 A_reconstructed:\n{}", reconstructed);
let difference = a - reconstructed;
println!("重建误差:\n{}", difference);

用 DMatix 的版本:

use nalgebra::DMatrix;
use nalgebra::linalg::SVD;


let a = DMatrix::<f64>::from_row_slice(3, 3, &[
    1.0, 2.0, 3.0,
    4.0, 5.0, 6.0,
    7.0, 8.0, 9.0
]);

let svd = SVD::new(a.clone(), true, true);
let u = svd.u.unwrap();
let sigma = svd.singular_values;
let vt = svd.v_t.unwrap();


println!("U:\n{:?}", u);
println!("Σ:\n{:?}", sigma);
println!("V^T:\n{:?}", vt);


// 验证重建: A = U * Σ * V^T
let sigma_matrix = DMatrix::from_diagonal(&sigma);
let reconstructed = u * sigma_matrix * vt;
println!("重建后的矩阵 A_reconstructed:\n{}", reconstructed);
let difference = a - reconstructed;
println!("重建误差:\n{}", difference);

jieba

https://github.com/messense/jieba-rs

jieba-rs = "*"

使用

let jieba = Jieba::new();
let sentence = "来到了南京市长江大桥";

// 分词
let words: Vec<&str> = jieba.cut(sentence, false);
assert_eq!(words, vec!["我们", "中", "出", "了", "一个", "叛徒"]);
// 搜索引擎方式分词
let words: Vec<&str> = jieba.cut_for_search(sentence, false);


// 词+词性
let tags = jieba.tag(sentence, false);
for tag in tags {
    println!("{},{}", tag.word, tag.tag);
}

// 词+索引
let tokens: Vec<Token> = jieba.tokenize(sentence, TokenizeMode::Default, false);
// TokenizeMode::Search

添加自定义词典

// 方式1: 读取 .txt 格式的文件作为词典
// dict.txt 内容:
// 创新办 100 nt
// 云计算 100 n

let dict_filename = "dict.txt";

let dict = fs::read_to_string(dict_filename).unwrap();
jieba.load_dict(&mut io::Cursor::new(dict.as_bytes())).unwrap();


// 方式2:在代码里面指定
let dict = "创新办 100 nt\n云计算 100 n";
jieba.load_dict(&mut io::Cursor::new(dict.as_bytes())).unwrap();


// 方式3: 使用 add_word
jieba.add_word("创新办", Some(100), Some("nr"));
jieba.add_word("云计算", Some(100), Some("n"));


// 使用:
let words: Vec<&str> = jieba.cut("李小福是创新办主任也是云计算方面的专家", true);

硬件相关

cpu 数量

// num_cpus = "*"
use num_cpus;

fn main() {
    println!("Number of logical cores is {}", num_cpus::get());
}

测试性能

[dependencies]

bench = "*"

lib.rs

#![feature(test)]
pub mod benchmarks;

benchmarks.rs

#![feature(test)]
extern crate test;

use test::Bencher;


#[bench]
fn bench_pcre2_is_match(b: &mut Bencher) {
    let pcre_re: pcre2::bytes::Regex = pcre2::bytes::Regex::new(r#"he.{0,2}o"#).unwrap();
    b.iter(|| pcre2_is_match(&pcre_re));
}

额外


// 如何匹配 Option
match opt {
  Some(i)=>{},
  None=>{}
}

// 如何匹配 Result
match res {
  Ok{t}=>{}
  Err(e)=>{}
}

改一个数字

fn myfun(a: &mut i32){
    *a+=1;
}
fn main() {
    let mut a=1;
    myfun(&mut a);
    println!("{}",a); // 为 2
}

密码学(待补充)

https://rust-lang-nursery.github.io/rust-cookbook/cryptography/encryption.html

ring::pbkdf2

SQLite 和 Postgres(待补充)

https://rust-lang-nursery.github.io/rust-cookbook/database/sqlite.html

调试/打log(待补充)

https://rust-lang-nursery.github.io/rust-cookbook/development_tools.html

网页编程、爬虫(待补充)

https://llever.com/rust-cookbook-zh/web/mime.zh.html

socket

https://llever.com/rust-cookbook-zh/net/server.zh.html

serde:序列化工具

// serde = { version = "1.0", features = ["derive"] }
// serde_json="*"
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

let point = Point { x: 1, y: 2 };

// 序列化后的结果 {"x":1,"y":2}
let serialized: String = serde_json::to_string(&point).unwrap();

// 反序列化
let deserialized: Point = serde_json::from_str(&serialized).unwrap();

参考资料

  • https://llever.com/rust-cookbook-zh/
  • rust的12个杀手级库 https://zhuanlan.zhihu.com/p/426329188

您的支持将鼓励我继续创作!