Skip to content
On this page

Rust进阶

1、collect

TIP

  • collect将一个数据转换成一个新的集合

1.1、定义

js
let numbers = vec![1, 2, 3, 4, 5];
let sum: Vec<i32> = numbers.iter().map(|x| x * x).collect(); 
println!("{:?}", sum);

1.2、zip + collect

js
let teams = vec![String::from("blue"), String::from("red")];
let inial = vec![1, 2];
let scores: HashMap<_, _> = teams.iter().zip(inial.iter()).collect();
println!("{:?}", scores);

// {"blue": 1, "red": 2}

1.3、repeat + collection

js
use std::{cell::RefCell, thread, iter::repeat};
let res: Vec<i32> = repeat(10).take(100).collect();

2、HashMap

2.1、定义

js
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);

2.2、entry

js
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);

// entry: 有key返回,无key创建
// or_insert: 有值返回,无值创建
scores.entry(String::from("Yellow")).or_insert(50);

2.3、动态创建

js
let text = "hello world from rust";
let mut map = HashMap::new();
for word in text.split_whitespace() {
    let count = map.entry(word).or_insert(0);
    *count += 1;
}
println!("map: {:?}", map);

3、测试

3.1、单元测试

js
fn main() {

}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

3.2、断言类型

3.2.1、assert(相等)

js
assert!(true); // ok
assert!(false); // false
// 测试失败提示
assert!(false, "测试结果true");

3.2.2、assert_eq(相等+表达式)

js
assert_eq!(1 + 2, 4, "测试结果1 + 2 = 3");
assert_eq!(1 + 2, 3, "测试结果{} + {} = {}", 1, 2, 3);

3.2.3、assert_ne(不等)

js
assert_ne!(1 + 2, 4, "测试结果1 + 2 = 3"); // ok

3.3、使用外部方法

TIP

使用use super::*;引入外部模块

js
fn eq(x: i32, y: i32, r: i32) -> bool {
    x + y == r
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert!(eq(1, 2, 3));
    }
}

3.4、集成测试

TIP

项目中必须同时包含lib.rsmain.rs, 否则会报错

js

4、结果输出

4.1、将结果输出到文件,错误打印

TIP

  • eprintln输出到标准错误
  • println输出到标准输出
js
use std::{fs, process};

fn main() {
  let result = fs::read_to_string("path.txt").unwrap_or_else(|err| {
    eprintln!("Failed to read file: {:?}", err);
    process::exit(1)
  });
  println!("{}", result);
}

运行:

shell
cargo run > output.txt

5、运算符重载

5.1、+重载

js
use std::ops;

#[derive(Debug)]
struct Complex {
    real: f64,
    imag: f64,
}

impl Complex {
    fn new(real: f64, imag: f64) -> Self {
        Complex {
            real,
            imag
        }
    }
}

impl ops::Add for Complex {
    type Output = Self;
    fn add(self, other: Complex) -> Self {
        Complex {
            real: self.real + other.real,
            imag: self.imag + other.imag,
        }
    }
}

fn main() {
    let c = Complex::new(1.0, 2.0);
    let d = Complex::new(3.0, 4.0);
    let sum = c + d;
    println!("{:?}", sum);
}

6、宏

6.1、规则宏(声明宏)

6.1.1、宏定义

示例1

js
macro_rules! my_macro {
    () => {
        println!("My macro!");
    };
}

fn main() {
    my_macro!();
}

示例2

js
macro_rules! log {
    ($level: expr, $($arg: tt)*) => {
        println!(concat!("[", $level, "] ", $($arg)*));
    };
}

6.1.2、带参数宏

js
macro_rules! add {
    ($x: expr, $y: expr) => {
        $x + $y
    };
}

6.1.3、多个参数宏

js
macro_rules! sum {
    ($x:expr) => {
        $x
    };
    ($x:expr, $($rest:expr),*) => {
        $x + sum!($($rest),*)
    };
}

6.1.4、数据结构定义

js
macro_rules! point {
    ($name: ident, $x: expr, $y: expr) => {
        struct $name {
            x: i32,
            y: i32,
        }

        impl $name {
            fn new(x: i32, y: i32) -> Self {
                $name { x, y }
            }

            fn get_x(&self) -> i32 {
                self.x
            }

            fn get_y(&self) -> i32 {
                self.y
            }
        }
    };
}
fn main() {
    point!(Point, 1, 2);
    let p = Point::new(1, 2);
    println!("x: {} y: {}", p.get_x(), p.get_y());
}

6.1.5、DSL

js
macro_rules! html_element {
    ($tag:expr, { $($attr:ident=$value:expr),* }, [$($content:expr),*]) => {{
        let mut element = String::new();
        element.push_str(&format!("<{} ", $tag));
        $(element.push_str(&format!("{}=\"{}\" ", stringify!($attr), $value));)*
        element.push_str(">");
        element.push_str(&format!("{}", html_content!($($content),*)));
        element.push_str(&format!("</{}>", $tag));
        element
    }};
}

macro_rules! html_content {
    ($($content:expr),*) => {
        format!($($content),*)
    };
    () => {
        String::new()
    };
}

fn main() {
    let name = "Alice";
    let age = 30;

    let html = html_element!(
        "div",
        {
            class="container",
            id="user-info",
            data="user-data"
        },
        [
            "Name: {}, Age: {}", name, age
        ]
    );

    println!("{}", html);
}

6.1.6、匹配模式宏

js
macro_rules! expr_match {
    ($e:expr) => {
        println!("The expression is {:?}", $e);
    };
    ($e:expr, $msg:expr) => {
        println!("{}: {:?}", $msg, $e);
    };
    ($e:expr, $msg:expr, $result:expr) => {
        println!("{}: {:?} => {:?}", $msg, $e, $result);
    };
}

6.2、过程宏(属性宏)

TIP

添加配置项Cargo.toml, 在lib.rs中定义宏

js
[lib]
proc_macro = true
path = "src/lib.rs"

6.2.1、简单宏

js
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn my_attribute(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let mut result = item.to_string();
    result.push_str(" // This is my custom attribute!");
    result.parse().unwrap()
}

main.rs

js
use rust_tools::my_attribute;

#[my_attribute]
fn hello() {}

fn main() {
    hello();
}

6.2.2、带参数的宏

js
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn my_attribute(attr: TokenStream, item: TokenStream) -> TokenStream {
    let function_name = attr.to_string();
    let mut result = item.to_string();
    result.push_str(&format!("fn {}() {{", function_name));
    result.push_str("println!(\"This is a custom function generated by attribute macro!\"); }");
    result.parse().unwrap()
}

main.rs

js
use rust_tools::my_attribute;

#[my_attribute(hello)]
fn dummy() {}

fn main() {
    hello();
}

6.2.3、自定义数据结构

js
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn my_struct(attr: TokenStream, item: TokenStream) -> TokenStream {
    let struct_name = attr.to_string();
    let mut result = item.to_string();
    result.push_str(&format!("struct {} {{", struct_name));
    result.push_str("data: i32, }");
    println!("{}-{}-{}", attr, item, result);
    result.parse().unwrap()
}

main.rs

js
use rust_tools::my_struct;

#[my_struct(Point)]
fn dummy() {}

fn main() {
    let point = Point { data: 12 };
    println!("{}", point.data);
}

6.3、函数宏

6.3.1、简单宏

js
use proc_macro::TokenStream;

#[proc_macro]
pub fn print_hello(_input: TokenStream) -> TokenStream {
    let output = "println!(\"Hello, macro!\");";
    output.parse().unwrap()
}

6.3.2、带参数的宏

js
use proc_macro::TokenStream;

#[proc_macro]
pub fn print_message(input: TokenStream) -> TokenStream {
    let message = input.to_string();
    let output = format!("println!(\"{}!\");", message);
    output.parse().unwrap()
}

main.rs

js
use rust_tools::print_hello;

fn main() {
    print_message!(111);
}

6.3.3、自定义数据结构

js
use proc_macro::TokenStream;

#[proc_macro]
pub fn my_struct(input: TokenStream) -> TokenStream {
    let struct_name = input.to_string();
    let output = format!("struct {} {{ data: i32 }}", struct_name);
    output.parse().unwrap()
}

6.3.4、自定义宏

lib.rs

js
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn;

#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // rust转字符串
    let ast = syn::parse(input).unwrap();
    impl_hello_macro(&ast)
}

fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let gen = quote! {
        trait HelloMacro {
            fn hello_macro();
        }
        impl HelloMacro for #name {
            fn hello_macro() {
                println!("Hello, Macro! My name is {}", stringify!(#name));
            }
        }
        impl #name {
            fn hello_macro2() {
                println!("Hello, Macro! My name is {}", stringify!(#name));
            }
        }
    };
    gen.into()
}

main.rs

js
use packages::HelloMacro;

#[derive(HelloMacro)]
struct Post{}
    
fn main() {
    Post::hello_macro2();
}

6.4、文档

6.4.1、文档格式

js
//! # my doc
//! 
//! my doc description
//! 

/// 计算一个数字加1后的值
/// 
/// x: 第一个参数
/// 
/// # Example
/// 
/// ```
///   let arg = 5;
///   let result = rust-tools::add(arg);
/// 
///   assert_eq!(6, result);
/// ```
pub fn add(x: i32) -> i32 {
    x + 1
}

6.4.2、展开导出

js
// 展开导出
pub use self::Kinds::{PrimaryColor, SecondaryColor};

pub mod Kinds {
    /// primary color
    pub enum PrimaryColor {
        Red,
        Yellow,
        Blue,
    }

    /// secondary color
    pub enum SecondaryColor {
        Orange,
        Green,
        Purple,
    }
}

6.4.3、文档预览

shell
cargo doc --open

6.4.4、文档生成

shell
cargo doc

6.5、工作区间

6.5.1、工作区间配置

目录结构

js
|-workspace
  |-add-one
    |-Cargo.toml
    |-src
  |-add-two
    |-Cargo.toml
    |-src
  |-Cargo.toml

创建工作区间

shell
cargo new add-one --lib

将lib模块添加到工作区间, 根目录下Cargo.toml

js
[workspace]
members = [
  "add-one",
  "add-two",
]

指定编译工作区间

shell
cargo build -p add-one

指定运行工作区间

shell
cargo run -p add-one

6.5.2、工作区间引用

add-one下的Cargo.toml

js
[dependecies]
add-two = { path = "../add-one" }

7、Mutex

7.1、Arc与Mutex

js
let counter = Arc::new(Mutex::new(0));
let mut handlers = vec![];
for _ in 0..10 {
  let counter = Arc::clone(&counter);
  let handler = thread::spawn(move || {
    let mut num = counter.lock().unwrap();
    *num += 1;
  });
  handlers.push(handler);
}

for handler in handlers {
  handler.join().unwrap();
}

let res = counter.lock().unwrap();
println!("Result:{}", res);

7.2、多线程共享Mutex

js
let counter = Arc::new(Mutex::new(0));
let mut handlers = vec![];
for _ in 0..10 {
  let counter = Arc::clone(&counter);
  let handler = thread::spawn(move || {
    let mut num = counter.lock().unwrap();
    *num += 1;
  });
  handlers.push(handler);
}

for handler in handlers {
  handler.join().unwrap();
}

let res = counter.lock().unwrap();
println!("Result:{}", res);

8、自定义Error

8.1、自定义错误

js
use std::error::Error;

#[derive(Debug)]
struct ErrorA {
  err: ErrorB,
}

impl std::fmt::Display for ErrorA {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    write!(f, "ErrorA")
  }
}

impl std::error::Error for ErrorA {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        Some(&self.err)
    }
}
    
fn error_a() -> Result<(), ErrorA> {
    Err(ErrorA {
        err: ErrorB
    })
}

#[derive(Debug)]
struct ErrorB;

impl std::fmt::Display for ErrorB {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    write!(f, "ErrorB")
  }
}

impl std::error::Error for ErrorB {}
    
fn main() -> Result<(), Box<dyn std::error::Error>>{
    match error_a() {
        Err(e) => {
            println!("{:?}", e);
            println!("{:?}", e.source());
        },
        _ => {}
    }
    Ok(())
}

8.2、错误转换

js
use std::error::Error;

#[derive(Debug)]
struct ErrorA;

impl std::fmt::Display for ErrorA {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    write!(f, "ErrorA")
  }
}

impl std::error::Error for ErrorA {}
    
fn error_a() -> Result<(), ErrorA> {
    Err(ErrorA)
}

#[derive(Debug)]
struct ErrorB;

impl std::fmt::Display for ErrorB {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    write!(f, "ErrorB")
  }
}

impl std::error::Error for ErrorB {}


// 为错误B实现错误A
impl From<ErrorA> for ErrorB {
    fn from(_: ErrorA) -> Self {
        ErrorB
    }
}
    
fn main() -> Result<(), ErrorB>{
    println!("{:#?}", ErrorB::from(ErrorA));
    error_a()?;
    Ok(())
}

9、类型转换

9.1、from into

js
enum Status {
    Working,
    Vacation,
    Absenteesim(i32)
}

impl From<i32> for Status {
    fn from(i: i32) -> Self {
        match i {
            0 => Status::Working,
            1..=3 => Status::Vacation,
            _ => Status::Absenteesim(i)
        }
    }
}

// 发工资
fn pay(mis_day: i32) -> i32 {
    let status = Status::from(mis_day);
    match status {
        Status::Working => 1000,
        Status::Vacation => 1000,
        Status::Absenteesim(_) => {
            let pays = 1000 - mis_day * 300;
            if pays < 0 {
                0
            } else {
                pays
            }
        },
    }
}

fn main() {
    println!("{}", pay(0));
    println!("{}", pay(3));
    println!("{}", pay(5));
}

9.2、try_from try_into

js
enum Status {
    Working,
    Vacation,
    Absenteesim(i32)
}

impl TryFrom<i32> for Status {
    type Error = ();
    
    fn try_from(i: i32) -> Result<Self, Self::Error> {
        match i {
            0 => Ok(Status::Working),
            1..=3 => Ok(Status::Vacation),
            _ => Ok(Status::Absenteesim(i))
        }
    }
    
}

// 发工资
fn pay(mis_day: i32) -> i32 {
    let status = Status::try_from(mis_day);
    match status {
        Ok(Status::Working) => 1000,
        Ok(Status::Vacation) => 1000,
        Ok(Status::Absenteesim(day)) => {
          let pays = 1000 - day * 300;
          if pays < 0 {
              0
          } else {
              pays
          }
        },
        _ => 0
    }
}

fn main() {
    println!("{}", pay(0));
    println!("{}", pay(3));
    println!("{}", pay(5));
}

是否为0判断

js
#[derive(Debug)]
struct Zero(u32);

#[derive(Debug)]
enum IsZero {
  Zero
}


impl TryFrom<u32> for Zero {
    type Error = IsZero;

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        if value == 0 {
            Err(IsZero::Zero)
        } else {
            Ok(Zero(value))
        }
    }
    
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let zero = Zero::try_from(0_u32);
    match zero {
        Ok(zero) => println!("1--{}", zero.0),
        Err(IsZero::Zero) => println!("1--zero"),
    }

    let some_type: Result<Zero, IsZero> = 0_u32.try_into();
    match some_type {
        Ok(zero) => println!("2--{}", zero.0),
        Err(IsZero::Zero) => println!("2--zero"),
    }

    Ok(())
}

Released under the MIT License.