Rust中应该尝试使用的12个杀手级库
1、clap
clap在写一些CLI工具时非常有用,在ripgrep
和Rust自己的Cargo
都在使用,clap编译后非常小,而且加载启动非常快。clap会帮你处理命令行参数,可以自定义友好提示,支持函数式操作,非常方便
use clap::App;
fn main() {
App::new("myapp")
.version("1.0")
.about("Does great things!")
.author("Kevin K.")
.get_matches();
}
$ myapp --help
myapp 1.0
Kevin K.
Does great things!
USAGE:
myapp [FLAGS]
FLAGS:
-h, --help Prints this message
-V, --version Prints version information
2、serde
和calp
一样,serde是一个功能丰富且性能非常好的通用序列化库。
生产中不建议自己造轮子读写文件,你可以先定义你要的数据类型,然后使用serde库加载读写文件。另外serde库可以帮你完成YAML
,JSON
的序列化和反序化,非常的方便
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 1, y: 2 };
// Convert the Point to a JSON string.
let serialized = serde_json::to_string(&point).unwrap();
// Prints serialized = {"x":1,"y":2}
println!("serialized = {}", serialized);
// Convert the JSON string back to a Point.
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
// Prints deserialized = Point { x: 1, y: 2 }
println!("deserialized = {:?}", deserialized);
}
3、reqwest
reqwest
和request
一样,是一个http
协议的客户端库,帮大多数人做了一些希望HTTP
客户端为他们做的许多事情,发请求,超时处理,多种格式支持,异步同步请求,代理等等
// This will POST a body of `{"lang":"rust","body":"json"}`
let mut map = HashMap::new();
map.insert("lang", "rust");
map.insert("body", "json");
let client = reqwest::Client::new();
let res = client.post("http://httpbin.org/post")
.json(&map)
.send()
.await?;
4、rayon
rayon是一个可以并行提供数据的库,它知道如何合理的拆分数据块并合理的使用cpu。比如你想在一个列表上并行使用map时就可以使用它。这就有意思了,在写一些CLI工具的时候很有用,并不是所有的编程语言都可以在命令行并行跑一些数据
use rayon::prelude::*;
fn sum_of_squares(input: &[i32]) -> i32 {
input.par_iter() // <-- just change that!
.map(|&i| i * i)
.sum()
}
5、slog and log
slog
是Rust中非常完整的一个日志库。很多有名的库也使用它来做日志库,比如 term
,json
等等。但是log
库正在成为Rust标准库的一部分,可以考虑切换到log
库上
let decorator = slog_term::PlainSyncDecorator::new(std::io::stdout());
let drain = slog_term::FullFormat::new(decorator).build().fuse();
let root_log = slog::Logger::root(drain, o!("version" => "0.5"));
let server_log = root_log.new(o!("host" => "localhost", "port" => "8080"));
let peer1_log =
server_log.new(o!("peer_addr" => "8.8.8.8", "port" => "18230"));
let peer2_log =
server_log.new(o!("peer_addr" => "82.9.9.9", "port" => "42381"));
info!(server_log, "starting");
info!(server_log, "listening");
debug!(peer2_log, "connected");
debug!(peer2_log, "message received"; "length" => 2);
debug!(peer1_log, "connected");
debug!(peer2_log, "response sent"; "length" => 8);
debug!(peer2_log, "disconnected");
debug!(peer1_log, "message received"; "length" => 2);
debug!(peer1_log, "response sent"; "length" => 8);
debug!(peer1_log, "disconnected");
info!(server_log, "exit");
6、itertools
写代码的时候,在List上加N多个操作符非常的繁琐,幸好有itertools
帮我们处理,方便的很,比如你要对一个list做map完后排序,排序完后分组,你要怎么做?
// using Itertools::fold_results to create the result of parsing
let irises = DATA.lines()
.map(str::parse)
.fold_ok(Vec::new(), |mut v, iris: Iris| {
v.push(iris);
v
});
let mut irises = match irises {
Err(e) => {
println!("Error parsing: {:?}", e);
std::process::exit(1);
}
Ok(data) => data,
};
// Sort them and group them
irises.sort_by(|a, b| Ord::cmp(&a.name, &b.name));
7、hyper
hyper是一个用Rust编写的快速HTTP实现(与用C编写的相反,C编写的涵盖了动态语言的性能)。 你会发现hyper几乎存在于您使用的每个高级库中,如果直接使用它,感觉有点像Netty
或Finagle
官网地址: https://hyper.rs/
use std::{convert::Infallible, net::SocketAddr};
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};
async fn handle(_: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new("Hello, World!".into()))
}
#[tokio::main]
async fn main() {
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let make_svc = make_service_fn(|_conn| async {
Ok::<_, Infallible>(service_fn(handle))
});
let server = Server::bind(&addr).serve(make_svc);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
}
8、Actix
不用hyper还能用什么? 当然是Actix了, 使用Actix会变得非常简单。一般来说也经常使用Actix而不是Hyper吧,因为Actix更高级,并且在构建服务上更成熟。 构建web可以直接在Hyper上使用Actix,除非你需要构建一些靠近协议层硬件层的服务,或者要写一个基于Hyper的库。
github: https://github.com/actix/actix-web
use actix_web::{get, web, App, HttpServer, Responder};
#[get("/{id}/{name}/index.html")]
async fn index(web::Path((id, name)): web::Path<(u32, String)>) -> impl Responder {
format!("Hello {}! id:{}", name, id)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(index))
.bind("127.0.0.1:8080")?
.run()
.await
}
9、PyO3
Python的Rust绑定。 包括运行Rust二进制文件中的Python代码并与之交互,以及编写本机Python模块。
可能你还不信,但是你可以看看hyperjson,一种由Rust的serde(之前文章介绍过) 支持的PythonJSON
库,github地址:https://github.com/mre/hyperjson
依靠Rust的安全性和serde的性能,几乎可以毫不费力地获得一个安全的并且是Python写的最快的JSON库之一,不得不服啊
那你怎么把它用到自己平时写bug上呢:总结三步:
1、找到一个好的Rust库
2、用PyO3包一下
3、开始体验速度
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
/// Formats the sum of two numbers as string.
#[pyfunction]
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
Ok((a + b).to_string())
}
/// A Python module implemented in Rust.
#[pymodule]
fn string_sum(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
Ok(())
}
10、proptest
proptest是一个非常好用的测试库,类似python里面的[Hypothesis](http://hypothesis.works/)
,妈妈再也不用担心我写单元测试了。
地址: https://lib.rs/crates/proptest
fn parse_date(s: &str) -> Option<(u32, u32, u32)> {
if 10 != s.len() { return None; }
if "-" != &s[4..5] || "-" != &s[7..8] { return None; }
let year = &s[0..4];
let month = &s[6..7];
let day = &s[8..10];
year.parse::<u32>().ok().and_then(
|y| month.parse::<u32>().ok().and_then(
|m| day.parse::<u32>().ok().map(
|d| (y, m, d))))
}
11、libloading
如果你想将Go或任何其他c-lib库混入Rust中来,libloading可能会非常合适。
首先必须承认的一个问题就是,Rust现在的生态和积累了数10年,20年的c/c++来说还相对较弱,要接受Rust生态中某些部分还没有准备就绪,而且要勇敢的善于使用别的语言库来完成你要使用Rust开发项目这样一项工作,这时候你用libloading就行了,会帮你绑定到Rust中的,如果你放弃了因为生态问题,那你就可能永远入不了坑了。
地址: https://docs.rs/libloading/0.5.0/libloading/
extern crate libloading as lib;
fn call_dynamic() -> lib::Result<u32> {
let lib = lib::Library::new("/path/to/liblibrary.so")?;
unsafe {
let func: lib::Symbol<unsafe extern fn() -> u32> = lib.get(b"my_func")?;
Ok(func())
}
}
这就是Rust中现阶段比较方便实用的12个库,Rust一直在发展,一直在包容并进,我们能做的就是使用它,多发现问题,多造轮子,多写一些没用的bug。