RUST实践
目录
密码
自定义密码
use rand::Rng;
pub fn main() {
const CHARSET: &[u8] = b"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();
println!("密码:{}", password);
}
Vector排序
整数
fn main() {
let mut vec = vec![1, 5, 8, 90, 89];
println!("前: {:?}", vec);
vec.sort();
println!("后: {:?}", vec);
assert_eq!(vec, vec![1, 5, 8, 90, 89]); // failed
}
浮点数
fn main() {
let mut vec = vec![1.8, 5.1, 8.9, 0.90, 8.9];
println!("前: {:?}", vec);
vec.sort_by(|a, b| a.partial_cmp(b).unwrap());
println!("后: {:?}", vec);
}
结构体
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
struct Person {
name: String,
age: u32
}
impl Person {
pub fn new(name: &str, age: u32) -> Self {
Person {
name: name.to_string(),
age
}
}
}
pub fn main() {
let mut peoples = vec![
Person::new("Zhang", 25),
Person::new("Liu", 60),
Person::new("Wang", 18),
];
println!("前: {:?}", peoples);
peoples.sort_by(|a, b| b.age.cmp(&a.age));
println!("后:{:?}", peoples);
}
命令行参数
use clap::{Arg, App};
pub fn main() {
let matches = App::new("测试程序")
.version("0.1.0")
.author("MUWUREN")
.about("Test clap")
.arg(Arg::with_name("file")
.short("f")
.long("file")
.takes_value(true)
.help("a cool file"))
.arg(Arg::with_name("num")
.short("n")
.long("num")
.takes_value(true)
.help("Five less than"))
.get_matches();
let myfile = matches.value_of("file").unwrap_or("input.txt");
println!("file: {}", myfile);
let num_str = matches.value_of("num");
match num_str {
None => println!("No idea!"),
Some(s) => {
match s.parse::<i32>() {
Ok(n) => println!("NUM: {}", n),
Err(e) => println!("Error: '{}', {}", s, e),
}
}
}
}
终端颜色
use ansi_term::{Colour, Style};
pub fn main() {
// color
println!("This is {} in color, {} in color and {} in color",
Colour::Red.paint("red"),
Colour::Blue.bold().paint("blue"),
Colour::Green.bold().paint("green")); // color and bold
// Bold
println!("{} and this is not", Style::new().bold().paint("BOLD"));
}
Tar
解压
use std::fs::File;
use flate2::read::GzDecoder;
use tar::Archive;
fn main() -> Result<(), std::io::Error> {
let path = "archive.tar.gz";
let tar_gz = File::open(path)?;
let tar = GzDecoder::new(tar_gz);
let mut archive = Archive::new(tar);
archive.unpack(".")?;
Ok(())
}
压缩
use std::fs::File;
use flate2::Compression;
use flate2::write::GzEncoder;
pub fn main() -> Result<(), std::io::Error>{
let tar_gz = File::create("ab.tar.gz")?;
let enc = GzEncoder::new(tar_gz, Compression::default());
let mut tar = tar::Builder::new(enc);
tar.append_dir_all("logs/", "/tmp/ccls/")?;
Ok(())
}
线程
短期线程
pub fn main() {
let arr = &[1, 25, 3, -4];
let max = find_max(arr);
assert_eq!(max, Some(25));
}
fn find_max(arr: &[i32]) -> Option<i32> {
const THRESHOLD: usize = 2;
if arr.len() <= THRESHOLD {
return arr.iter().cloned().max();
}
let mid = arr.len() / 2;
let (left, right) = arr.split_at(mid);
crossbeam::scope(|s| {
let thread_l = s.spawn(|_| find_max(left));
let thread_r = s.spawn(|_| find_max(right));
let max_l = thread_l.join().unwrap()?;
let max_r = thread_r.join().unwrap()?;
Some(max_l.max(max_r))
}).unwrap()
}
管道
extern crate crossbeam;
extern crate crossbeam_channel;
use crossbeam_channel::bounded;
use std::thread;
use std::time::Duration;
pub fn main() {
let (snd1, rcv1) = bounded(1);
let (snd2, rcv2) = bounded(1);
let n_msgs = 4;
let n_workers = 2;
crossbeam::scope(|s| {
s.spawn(|_| {
for i in 0..n_msgs {
snd1.send(i).unwrap();
println!("Source sent {}", i);
}
// close channel
drop(snd1);
});
for _ in 0..n_workers {
let (sendr, recvr) = (snd2.clone(), rcv1.clone());
s.spawn(move |_| {
thread::sleep(Duration::from_secs(1));
for msg in recvr.iter() {
println!("Worker {:?} received {}", thread::current().id(), msg);
sendr.send(msg * 2).unwrap();
}
});
}
drop(snd2);
for msg in rcv2.iter() {
println!("Sink received {}", msg);
}
}).unwrap();
}
无限容量管道
use std::{thread, time};
use crossbeam_channel::unbounded;
pub fn main() {
let (snd, rcv) = unbounded();
let n_msgs = 5;
crossbeam::scope(|s| {
s.spawn(|_| {
for i in 0..n_msgs {
snd.send(i).unwrap();
thread::sleep(time::Duration::from_secs(1));
}
});
}).unwrap();
for _ in 0..n_msgs {
let msg = rcv.recv().unwrap();
println!("{}", msg);
}
}
全局变量
use error_chain::error_chain;
use lazy_static::lazy_static;
use std::sync::Mutex;
error_chain!{ }
lazy_static! {
static ref FRUIT: Mutex<Vec<String>> = Mutex::new(Vec::new());
}
fn insert(fruit: &str) -> Result<()> {
let mut db = FRUIT.lock().map_err(|_| "Failed to acquire MutexGuard!")?;
db.push(fruit.to_string());
Ok(())
}
pub fn main() -> Result<()> {
insert("apple")?;
insert("orange")?;
insert("peach")?;
{
let db = FRUIT.lock().map_err(|_| "Failed TO acquire MutexGuard")?;
db.iter().enumerate().for_each(|(i, item)| println!("{}: {}", i, item));
}
insert("grape")?;
Ok(())
}
SHA计算
use walkdir::WalkDir;
use std::fs::File;
use std::io::{BufReader, Read, Error};
use threadpool::ThreadPool;
use std::path::Path;
use std::sync::mpsc::channel;
use ring::digest::{Context, Digest, SHA256};
fn is_iso(entry: &Path) -> bool {
match entry.extension() {
Some(e) if e.to_string_lossy().to_lowercase() == "iso" => true,
_ => false
}
}
fn compute_digest<P: AsRef<Path>>(filepath: P) -> Result<(Digest, P), Error> {
let mut buf_reader = BufReader::new(File::open(&filepath)?);
let mut context = Context::new(&SHA256);
let mut buffer = [0; 1024];
loop {
let count = buf_reader.read(&mut buffer)?;
if count == 0 {
break;
}
context.update(&buffer[..count]);
}
Ok((context.finish(), filepath))
}
pub fn main() -> Result<(), Error> {
let pool = ThreadPool::new(num_cpus::get());
let (tx, rx) = channel();
for entry in WalkDir::new("/home/nsfoxer/ISO")
.follow_links(true)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| !e.path().is_dir() && is_iso(e.path())) {
let path = entry.path().to_owned();
let tx = tx.clone();
pool.execute(move || {
let digest = compute_digest(path);
tx.send(digest).expect("Could not send data!");
});
}
drop(tx);
for t in rx.iter() {
let (sha, path) = t?;
println!("{:?}, {:?}", sha, path);
}
Ok(())
}
绘图
朱利亚集 f(n+1)=f(n)*f(n)+c
use error_chain::error_chain;
use std::sync::mpsc::{channel, RecvError};
use threadpool::ThreadPool;
use num::complex::Complex;
use image::{ImageBuffer, Pixel, Rgb};
error_chain! {
foreign_links {
MpscRecv(RecvError);
Io(std::io::Error);
}
}
fn wavelength_to_rgb(wavelength: u32) -> Rgb<u8> {
let wave = wavelength as f32;
let (r, g, b) = match wavelength {
380..=439 => ((440.-wave)/(440.-380.), 0.0, 1.0),
440..=489 => (0.0, (wave - 440.) / (490. - 440.), 1.0),
490..=509 => (0.0, 1.0, (510. - wave) / (510. - 490.)),
510..=579 => ((wave - 510.) / (580. - 510.), 1.0, 0.0),
580..=644 => (1.0, (645. - wave) / (645. - 580.), 0.0),
645..=780 => (1.0, 0.0, 0.0),
_ => (0.0, 0.0, 0.0),
};
let factor = match wavelength {
380..=419 => 0.3 + 0.7 * (wave - 380.) / (420. - 380.),
701..=780 => 0.3 + 0.7 * (780. - wave) / (780. - 700.),
_ => 1.0,
};
let (r, g, b) = (normalize(r, factor), normalize(g, factor), normalize(b, factor));
Rgb::from_channels(r, g, b, 0)
}
fn julia(c: Complex<f32>, x: u32, y:u32, u32, height: u32, max_iter: u32) -> u32 {
let width = width as f32;
let height = height as f32;
let mut z = Complex {
re: 3.0 * (x as f32 - 0.5*width) / width,
im: 2.0 * (y as f32 - 0.5*height) / height,
};
let mut i = 0;
for t in 0..max_iter {
if z.norm() >= 2.0 {
break;
}
z = z*z+c;
i = t;
}
i
}
fn normalize(color: f32, factor: f32) -> u8 {
((color * factor).powf(0.8) * 255.) as u8
}
pub fn main() -> Result<()> {
let (width, height) = (1920, 1080);
let mut img = ImageBuffer::new(width, height);
let iterations = 300;
let c = Complex::new(-0.8, 0.156);
let pool = ThreadPool::new(num_cpus::get());
let (tx, rx) = channel();
for y in 0..height {
let tx = tx.clone();
pool.execute(move || for x in 0..width {
let i = julia(c, x, y, width, height, iterations);
let pixel = wavelength_to_rgb(380+i*400/iterations);
tx.send((x, y, pixel)).expect("Could not send data!");
});
}
for _ in 0..(width*height) {
let (x, y, pixel) = rx.recv()?;
img.put_pixel(x, y, pixel);
}
let _ = img.save("output.png").unwrap();
Ok(())
}
改变数组
use rayon::prelude::*;
fn main() {
let mut arr = [0, 7, 9, 11];
arr.par_iter_mut().for_each(|p| *p -= 1);
println!("{:?}", arr);
}
数组测试
use rayon::prelude::*;
pub fn main() {
let arr = vec![1, 7, 9, 11];
// any() --> 只要有一个
// all() --> 全部都要
assert!(!arr.par_iter().any(|n| (*n % 2) == 0));
assert!(arr.par_iter().all(|n| (*n % 2) != 0));
// 查找到第一个元素就返回,但不一定是vec的第一个元素
assert_eq!(arr.par_iter().find_any(|&&x| x == 9), Some(&9));
}
排序
use rand::{Rng, thread_rng};
use rand::distributions::Alphanumeric;
use rayon::prelude::*;
pub fn main() {
let mut arr = vec![String::new(); 100];
arr.par_iter_mut().for_each(|p| {
let mut rng = thread_rng();
*p = (0..50).map(|_| rng.sample(&Alphanumeric)).map(char::from).collect()
});
arr.par_sort_unstable();
println!("{:?}", arr);
}
Map-Reduce
use rayon::prelude::*;
struct Person {
age: u32,
}
pub fn main() {
let v: Vec<Person> = vec![
Person { age: 23 },
Person { age: 19 },
Person { age: 42 },
Person { age: 17 },
Person { age: 17 },
Person { age: 31 },
Person { age: 30 },
];
let num_over_30 = v.par_iter().filter(|&x| x.age > 30).count() as f32;
let sum_over_30 = v
.par_iter()
.map(|x| x.age)
.filter(|&x| x > 30)
.reduce(|| 0, |x, y| x + y);
let alt_sum_30: u32 = v.par_iter().map(|x| x.age).filter(|&x| x > 30).sum();
let avg_over_30 = sum_over_30 as f32 / num_over_30;
let alt_avg_over_30 = alt_sum_30 as f32 / num_over_30;
assert!((avg_over_30 - alt_avg_over_30).abs() < std::f32::EPSILON);
println!("The average age of people older than 30 is {}", avg_over_30);
}
缩略图
use error_chain::error_chain;
use std::path::Path;
use std::fs::create_dir_all;
use error_chain::ChainedError;
use glob::{glob_with, MatchOptions};
use image::{ImageError, imageops::FilterType};
use rayon::prelude::*;
error_chain! {
foreign_links {
Image(ImageError);
Io(std::io::Error);
Glob(glob::PatternError);
}
}
pub fn main() -> Result<()>{
let options: MatchOptions = Default::default();
let files: Vec<_> = glob_with("*.jpg", options)?
.filter_map(|x| x.ok())
.collect();
if files.len() == 0 {
error_chain::bail!("No .jpg was founded!");
}
let thumb_dir = "thumbnails";
create_dir_all(thumb_dir)?;
println!("Save {} thumbnails into `{}`...", files.len(), thumb_dir);
let image_failures: Vec<_> = files
.par_iter()
.map(|path|{
make_thumbnail(path, thumb_dir, 300)
.map_err(|e| e.chain_err(|| path.display().to_string()))
})
.filter_map(|x| x.err())
.collect();
image_failures.iter().for_each(|x| println!("{}", x.display_chain()));
println!("{} thumbnails saved successfully", files.len() - image_failures.len());
Ok(())
}
fn make_thumbnail<PA, PB>(original: PA, thumb_dir: PB, longest_edge: u32) -> Result<()>
where
PA: AsRef<Path>,
PB: AsRef<Path>,
{
let img = image::open(original.as_ref())?;
let file_path = thumb_dir.as_ref().join(original);
Ok(img.resize(longest_edge, longest_edge, FilterType::Nearest)
.save(file_path)?)
}
密码
SHA256
use error_chain::error_chain;
use data_encoding::HEXUPPER;
use ring::digest::{Context, Digest, SHA256};
use std::fs::File;
use std::io::{BufReader, Read, Write};
error_chain! {
foreign_links {
Io(std::io::Error);
Decode(data_encoding::DecodeError);
}
}
fn sha256_digest<R: Read>(mut reader: R) -> Result<Digest> {
let mut context = Context::new(&SHA256);
let mut buffer = [0; 1024];
loop {
let count = reader.read(&mut buffer)?;
if count == 0 {
break;
}
context.update(&buffer[..count]);
}
Ok(context.finish())
}
pub fn main() -> Result<()>{
let path = "file.txt";
let mut output = File::create(path)?;
write!(output, "We will generate a digest of the text")?;
let input = File::open(path)?;
let reader = BufReader::new(input);
let digest = sha256_digest(reader)?;
println!("SHA-256 digest is {}", HEXUPPER.encode(digest.as_ref()));
Ok(())
}
HMAC签名和校验
use ring::{hmac, rand};
use ring::rand::SecureRandom;
use ring::error::Unspecified;
pub fn main() -> Result<(), Unspecified> {
let mut key_value = [0u8; 48];
let rng = rand::SystemRandom::new();
rng.fill(&mut key_value)?;
let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value);
let message = "Legitimate and import message";
println!("{}", &message);
let signature = hmac::sign(&key, message.as_bytes());
println!("{:?}", &signature);
hmac::verify(&key, message.as_bytes(), signature.as_ref())?;
Ok(())
}
密码salt和hash
use data_encoding::HEXUPPER;
use ring::rand::SecureRandom;
use ring::{digest, pbkdf2, rand};
use ring::error::Unspecified;
use std::num::NonZeroU32;
pub fn main() -> Result<(), Unspecified> {
const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN;
let n_iter = NonZeroU32::new(100_000).unwrap();
let rng = rand::SystemRandom::new();
let mut salt = [0u8; CREDENTIAL_LEN];
rng.fill(&mut salt)?;
let password = "Guess Me If You Can";
let mut pbkdf2_hash = [0u8; CREDENTIAL_LEN];
pbkdf2::derive(
pbkdf2::PBKDF2_HMAC_SHA512,
n_iter,
&salt,
&password.as_bytes(),
&mut pbkdf2_hash
);
println!("Salt: {:?}", &salt);
println!("Salt: {}", HEXUPPER.encode(&salt));
println!("PBKDF2 hash: {:?}", &pbkdf2_hash);
println!("PBKDF2 hash: {}", HEXUPPER.encode(&pbkdf2_hash));
let should_succeed = pbkdf2::verify(
pbkdf2::PBKDF2_HMAC_SHA512,
n_iter,
&salt,
password.as_bytes(),
&pbkdf2_hash,
);
let wrong_password = "Wrong Password";
let should_fail = pbkdf2::verify(
pbkdf2::PBKDF2_HMAC_SHA512,
n_iter,
&salt,
wrong_password.as_bytes(),
&pbkdf2_hash,
);
assert!(should_succeed.is_ok());
assert!(!should_fail.is_ok());
Ok(())
}
数据库
创建
use rusqlite::{Connection, Result};
pub fn main() -> Result<()> {
let conn = Connection::open("cat.db")?;
conn.execute(
"create table if not exists cat_colors(
id integer primary key,
name text not null unique
)",
[]
)?;
conn.execute(
"create table if not exists cats (
id integer primary key,
name text not null,
color_id integer not null references cat_colors(id)
)",
[]
)?;
Ok(())
}
插入数据 事务
use rusqlite::{Connection, Result};
pub fn main() -> Result<()> {
let mut conn = Connection::open("cat.db")?;
successful_tx(&mut conn)?;
let res = rolled_back_tx(&mut conn);
assert!(res.is_err());
Ok(())
}
fn successful_tx(conn: &mut Connection) -> Result<()> {
let tx = conn.transaction()?;
tx.execute("delete from cat_colors", [])?;
tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
tx.execute("insert into cat_colors (name) values (?1)", &[&"blue"])?;
tx.commit()
}
fn rolled_back_tx(conn: &mut Connection) -> Result<()> {
let tx = conn.transaction()?;
tx.execute("delete from cat_colors", [])?;
tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
tx.commit()
}
时间
测量运行时间
use std::time::{Duration, Instant};
use std::thread;
pub fn main() {
let start = Instant::now();
expensive_fun();
let duration = start.elapsed();
println!("消耗时间: {:?}", duration);
}
fn expensive_fun() {
thread::sleep(Duration::from_secs(1));
}