小白学习 Rust

本文介绍了Rust语言的学习资源、官方文档、安装步骤、构建二进制文件、crate类型、FFI与C字符串处理、JSON操作、创建HTTP服务、迷你Redis和日志管理。此外,还提及了编码规范和基本操作技巧。
摘要由CSDN通过智能技术生成

Rust

1. 学习文档

Rust 文档网 - Rust 官方文档中文教程

Rust 中文文档 | Rust 文档网 (rustwiki.org)

Comprehensive Rust

Rust 参考手册 中文版 (rustwiki.org)

Rust Cookbook 中文版 (rustwiki.org)

Rust语言圣经(Rust Course)

Cargo 手册 中文版 (rustwiki.org)

通过例子学 Rust 中文版 (rustwiki.org)

Docs.rs – crates查询

Rust 程序设计语言 简体中文版

2. 下载

Other Installation Methods - Rust Forge (rust-lang.org)

下载官网

验证:rustup --versionrustc --versioncargo --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 --releasetarget/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. 链接自定义库

  • Rust 调用标准C接口的自定义c/c++库,FFI详解_rust调用c++库

  • 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数据

  • 学习文档

  • 从前端视角从零带你用 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);
    }
    

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  
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值