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码有这些编码模式
- 数字模式:用于编码0-9的纯数字
- 字母数字模式:0-9a-zA-Z
- 字节模式
- 日本汉字模式
纠错级别:
- 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(线性代数包)库来提升性能。
- 依赖
OpenBLAS
,OpenBLAS
是 C 和汇编语言写的高性能计算库。它适配各种操作系统和处理器,还能自动根据指令集来优化。Numpy
和SciPy
也是基于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