Rust
1. 学习文档
Rust 中文文档 | Rust 文档网 (rustwiki.org)
2. 下载
验证:
rustup --version
、rustc --version
、cargo --version
更新:
rustup update
卸载:
rustup self uninstall
3. 构建 Rust 二进制文件
-
在你要复制的示例上点击“复制到剪贴板”按钮。
-
使用
cargo new exercise
为你的代码新建一个exercise/
目录:$ cargo new exercise Created binary (application) `exercise` package
-
导航至
exercise/
并使用cargo run
构建并运行你的二进制文件:$ cd exercise $ cargo run Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise) Finished dev [unoptimized + debuginfo] target(s) in 0.75s Running `target/debug/exercise` Hello, world!
-
将
src/main.rs
中的样板代码替换为你自己的代码。例如, 使用上一页中的示例,将src/main.rs
改为:fn main() { println!("Edit me!"); }
-
使用
cargo run
构建并运行你更新后的二进制文件:$ cargo run Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise) Finished dev [unoptimized + debuginfo] target(s) in 0.24s Running `target/debug/exercise` Edit me!
-
使用
cargo check
快速检查项目是否存在错误;使用cargo build
只进行编译,而不运行。你可以在target/debug/
中找到常规调试 build 的输出。使用cargo build --release
在target/release/
中生成经过优化的 发布 build。 -
你可以通过修改
Cargo.toml
为项目添加依赖项。当你运行cargo
命令时,系统会自动为你下载和编译缺失的依赖项。
注意:
- rust项目中默认一个二进制编译文件为src/main.rs。如果需要crate多个二进制编译文件可以在src目录下创建一个bin文件夹,在该目录下可以同时创建多个二进制编译文件。
- rust项目默认库文件为src/lib.rs。
- rust有工作区的概念可以见多个工作区来进行编译。
4. Rust简单信息
-
crate-type
bin
=> 二进制可执行crate
, 编译出的文件为二进制可执行文件。lib
=> 库crate
。rlib
=> Rust Library 特定静态中间库格式。如果只是纯 Rust 代码项目之间的依赖和调用,那么,用 rlib 就能完全满足使用需求。dylib
=> 动态库。cdylib
=> C规范动态库。staticlib
=> 静态库。
-
宏
-
过程宏
-
属性宏
-
#[link]
:// name为库名;kind指定库的类型,默认为动态库,后缀`so/dll/dylib/a` #[link(name = "foo", kind = "static")] // 标记静态库 #[link(name = "foo", kind = "static")] // osx的一种特殊库 #[link(name = "CoreFoundation", kind = "framework")]
-
#[wasm_bindgen]
:属性表明它下面的函数可以在 JavaScript 和 Rust 中访问
-
#[no_mangle]
:表示生成的函数名经过编译后依然为foo,从而和c语言保持一致; extern “C” :该函数可以提供给其他库或者语言调用,并且采用c语言的调用约定
-
#![allow(dead_code)]
:该属性用于隐藏对未使用代码的警告
-
#[allow(non_camel_case_types)]
:屏蔽没有驼峰命名的警告
-
-
-
标记
-
extern
: 标记的块表示外来的,默认"C" ABI
就是库中来的,完整应该是:extern "C" { }
-
pub
: 公共的,外部可访问的
-
-
重要特征
-
build.rs
构建脚本use std::env; use std::path::PathBuf; fn main() { let dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); println!( "cargo:rustc-link-search=native={}", dir.join("native").display() ); println!("cargo:rustc-link-lib=static=rust_1"); }
-
Cargo.toml
[package] name = "result_error_handle" version = "0.1.0" edition = "2021" # 添加库 [dependencies] thiserror = "1.0" # 简化创建自定义错误类型 anyhow = "1.0" # 为错误添加背景信息 wasm-bindgen = "0.2.84" # WebAssembly的依赖,rust与js互动依赖 ws = "0.9.2" # websocket依赖 env_logger = "0.10.0" # 日志依赖 [dev-dependencies] wasm-bindgen-test = "0.3.34" [profile.release] opt-level = "s" # rustc优化代码的大小为"small"
5. 链接自定义库
-
C/C++
(linux与windows下的编译文件对应关系):.o
=>.obj
=> 目标文件.a
=>.lib
=> 静态链接库.so
=>.dll
=> 动态链接库
-
示例(目录native下有一个
librust_lib.rlib
文件):// build.rs use std::env; use std::path::PathBuf; fn main() { // .rlib 路径 println!("cargo:rustc-link-search=./native"); } // main.rs extern crate rust_lib pub fn main() { // 调用rust_lib库下的方法 rust_lib::say_hello() }
6. C字符串与Rust字符串转换
-
传递源自 Rust 的 C 字符串:
use std::ffi::{CString, CStr}; use std::os::raw::c_char; fn work(data: &CStr) { extern "C" { fn work_with(data: *const c_char); } unsafe { work_with(data.as_ptr()) } } let s = CString::new("data data data data").expect("CString::new failed"); work(&s);
-
将外部 C 字符串转换为 Rust :
String
use std::ffi::CStr; use std::os::raw::c_char; extern "C" { fn my_string() -> *const c_char; } fn my_string_safe() -> String { let cstr = unsafe { CStr::from_ptr(my_string()) }; // 获取写时复制 Cow<'_, str>,然后保证新拥有所有权的字符串分配 String::from_utf8_lossy(cstr.to_bytes()).to_string() } println!("string: {}", my_string_safe());
7. rust操作json数据
-
学习文档
-
crate
[dependencies] # 这里新增 preserve_order 特性 --> 保证初始顺序 --> IndexMap<K, V> serde_json = {version = "1.0.104", features = ["preserve_order"]}
// preserve_order 特性的实现 #[cfg(not(feature = "preserve_order"))] type MapImpl<K, V> = BTreeMap<K, V>; #[cfg(feature = "preserve_order")] type MapImpl<K, V> = IndexMap<K, V>;
8. rust创建http服务
-
学习文档
-
例子
Cargo.toml
# Cargo.toml [package] name = "rs_http_server" version = "0.1.0" edition = "2021" [dependencies] hyper = { version = "0.14", features = ["full"] } tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1.0"
main.rs
// main.rs use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Request, Response, Server}; use serde::{Deserialize, Serialize}; use std::convert::Infallible; #[derive(Debug, Serialize, Deserialize)] struct Sum { a: isize, b: isize, c: isize, } #[derive(Serialize, Deserialize)] struct ResponseResult<T> { code: i32, result: Option<T>, error: Option<String>, } impl<T> ResponseResult<T> { fn success(result: T) -> Self { ResponseResult { code: 200, result: Some(result), error: None, } } fn error(error: String) -> Self { ResponseResult { code: 0, result: None, error: Some(error), } } } async fn handler(req: Request<Body>) -> Result<Response<Body>, hyper::Error> { let response = Response::builder() .header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") .header("Access-Control-Allow-Headers", "Content-Type"); match (req.method(), req.uri().path()) { (&hyper::Method::POST, "/check") => { let whole_body = hyper::body::to_bytes(req.into_body()).await.unwrap(); let body_str = String::from_utf8(whole_body.to_vec()).unwrap(); println!("{:?}", &body_str); let sum: Sum = match serde_json::from_str(&body_str) { Ok(contents) => contents, Err(error) => { let response_result = ResponseResult::<i32>::error(error.to_string()); let error_message = serde_json::to_string(&response_result).unwrap(); return Ok(response .body(Body::from(error_message.to_string())) .unwrap()); } }; println!("{:?}", &sum); let mut result = false; if sum.a + sum.b == sum.c { result = true } let response_body = serde_json::to_string(&ResponseResult::<bool>::success(result)).unwrap(); Ok(response .body(Body::from(response_body.to_string())) .unwrap()) } _ => { let error_message = serde_json::to_string(&ResponseResult::<i32>::error(String::from("Not Found"))) .unwrap(); Ok(response .body(Body::from(error_message.to_string())) .unwrap()) } } } #[tokio::main] async fn main() { let addr = ([0, 0, 0, 0], 9000).into(); let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handler)) }); let server = Server::bind(&addr) .serve(make_svc) .with_graceful_shutdown(async { tokio::signal::ctrl_c().await.unwrap(); println!("Ctrl-C received, shutting down"); }); println!("Listening on http://{}", addr); if let Err(e) = server.await { eprintln!("Server error: {}", e); } }
9. mini-redis
-
下载
cargo install mini-redis
-
使用
# 服务端启动 mini-redis-server # 客户端使用 mini-redis-cli set foo 1 mini-redis-cli get foo
-
代码实现
// server.rs use bytes::Bytes; use mini_redis::{Connection, Frame}; use std::collections::HashMap; use std::process; use std::sync::{Arc, Mutex}; use tokio::net::{TcpListener, TcpStream}; type RedisDBType = Arc<Mutex<HashMap<String, Bytes>>>; #[tokio::main] async fn main() { let listener = TcpListener::bind("127.0.0.1:6379").await.unwrap(); println!("Listening ..."); let db = Arc::new(Mutex::new(HashMap::new())); tokio::spawn(async move { handle_ctrl_c().await }); loop { let (socket, _) = listener.accept().await.unwrap(); // 将 handle 克隆一份 let db = db.clone(); tokio::spawn(async move { process(socket, db).await; }); } } async fn process(socket: TcpStream, db: RedisDBType) { use mini_redis::Command::{self, Get, Set}; let mut connection = Connection::new(socket); while let Some(frame) = connection.read_frame().await.unwrap() { let response = match Command::from_frame(frame).unwrap() { Set(cmd) => { println!( "Set key: {:?} value: {:?}", cmd.key().to_string(), String::from_utf8(cmd.value().to_vec()).unwrap() ); let mut db = db.lock().unwrap(); db.insert(cmd.key().to_string(), cmd.value().clone()); Frame::Simple("OK".to_string()) } Get(cmd) => { println!("Get key: {:?}", cmd.key().to_string()); let db = db.lock().unwrap(); if let Some(value) = db.get(cmd.key()) { Frame::Bulk(value.clone()) } else { Frame::Null } } cmd => panic!("unimplemented {:?}", cmd), }; connection.write_frame(&response).await.unwrap(); } } async fn handle_ctrl_c() { tokio::signal::ctrl_c().await.unwrap(); println!("Ctrl-C received, shutting down"); process::exit(0); }
client
实现:消息传递 - Rust语言圣经(Rust Course)
10. logger
-
dependences
[dependences] log = "0.3" env_logger = "0.10"
-
example
use env_logger::{Builder, Env}; use log::{debug, error, info, log_enabled, Level}; fn main() { // 注意,env_logger 必须尽可能早的初始化 // 修改默认的日志级别 Builder::from_env(Env::default().default_filter_or("info")).init(); debug!("this is a debug {}", "message"); error!("this is printed by default"); if log_enabled!(Level::Info) { let x = 3 * 4; // expensive computation info!("the answer was: {}", x); } }
Coding Guidelines
FAQ
1. 字符串后追加字符串
-
使用
+
运算符:fn main() { let mut str1 = String::from("Hello"); let mut str2 = String::from("World"); // 使用 + 运算符将两个字符串合并 let result = str1 + &str2; println!("result: {}", result); // 输出:HelloWorld }
-
使用 std::string::String 类的 push_str 方法:
fn main() { let mut str1 = String::from("Hello"); let mut str2 = String::from("World"); // 使用 push_str 方法将 str2 追加到 str1 后面 str1.push_str(&str2); println!("result: {}", str1); // 输出:HelloWorld }