前端学 Rust 进阶

86 阅读6分钟

参考:www.go-edu.cn/

IO操作

  • Read:用于从输入流读取字节数据
  • WriteL:用于向输出流中写入数据,包含字节数据和 UTF-8 数据两种格式

读(输入)

  • std::io::stdin 返回标准输入流的句柄
    • read_line:不会读取空行
    • unwrap:简化回复错误的处理
    let mut in_words = String::new();
    let result = std::io::stdin().read_line(&mut in_words).unwrap();
    
    println!("输入的是{}", in_words);
    println!("读取的字节数{}", result);

写入

  • std::io::stdout() 返回标准输出流的句柄
    • write 是标准输出流 stdout 句柄上的一个方法,用于标准输出流中写入字节流的内容,不会追加换行符
    let result1 = std::io::stdout().write("hello world".as_bytes()).unwrap();
    println!("写入的字节数{}", result1);

文件系统

Rust语言使用结构体File来描述/展现一个文件

所有对结构体File的操作方法都会返回一个Result枚举。

以下是一些常用的文件方法

模块方法说明
std::fs::Fileopen()静态方法,以只读模式打开文件
std::fs::Filecreate()静态方法,以可写模式打开文件。如果文件存在则清空旧内容 如果文件不存在则新建
std::fs::remove_fileremove_file()从文件系统中删除某个文件
std::fs::OpenOptionsappend()设置文件模式为追加
std::io::Writeswrite_all()将buf中的所有内容写入输出流
std::io::Readread_to_string()读取所有内容转换为字符串后追加到buf中

闭包

  • 闭包,可以在没有标注的情况下运行,可以移动和借用
    • 引用 &T
    • 可变 &mut
    • 值 T
  • 闭包是一个匿名函数
    let add = |x: i32, y: i32| { x + y };
    println!("{}", add(3, 4));

    let num = 2;
    let add2 = |x| {x + num};
    println!("{}", add2(3))

Rust 多线程

主线程执行完后,子线程不再执行

    thread::spawn(|| {
        for i in 1..10 {
            println!("子线程{}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    for i in 1..5 {
        println!("主线程{}", i);
        thread::sleep(Duration::from_millis(1));
    }

等待子线程执行

    let handler = thread::spawn(|| {
        for i in 1..10 {
            println!("子线程{}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    for i in 1..5 {
        println!("主线程{}", i);
        thread::sleep(Duration::from_millis(1));
    }
    handler.join().unwrap();

错误处理

panic!()抛出一个异常

使用 unwarp 抛出异常,和 expect 区别是,可以自定义报错的信息,接收一个 msg:&str

    let output = is_even(3).unwrap();
    println!("{:?}", output);
    
    fn is_even(number: i32) -> Result<bool, String>{
    return if number % 2 == 0 { Ok((true)) } else { Err(("错误".to_string())) };
}

模块

mod module_name {
    fn function_name(){
    
    }
}

// pub 关键字
pub mod module_name {
    pub fn function_name(){
    
    }
    // 私有方法
    fn function_name(){
    
    }
    // 使用模块
    use 公开模块::函数名称
}

创建模块

cargo new --lib mylib
  • lib
pub fn add(left: usize, right: usize) -> usize {
    left + right
}

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

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}
  • toml
[package]
name = "module_test"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
mylib={ path = "../module_test/mylib/" }

  • main
use ::mylib::*;
fn main() {
    let num = add(1, 2);
    println!("{}", num)
}

类型系统

    let spend = 1;
    let cost = spend as f64;
    println!("{}", cost);

    let x = 1u8;
    let y = 2u16;

使用别名(驼峰)

type MyU64
let num: MyU64 = 5 as MyU64;

类型转换

From

From trait 允许一种类型定义 “怎么根据另一种类型生成自己”,因此它提供了一种类型转换的简单机制。在标准库中有无数 From 的实现,规定原生类型及其他常见类型的转换功能。

    let s1 = "从0到Go语言微服务架构师";
    let s2 = String::from(s1);


#[derive(Debug)]
struct MyNumber {
    num: i32,
}

impl From<i32> for MyNumber {
    fn from(item: i32) -> Self {
        MyNumber { num: item }
    }
}
fn main() {
    let my_number = MyNumber { num: 1 };
    println!("{:?}", my_number);
}

Into

Into trait 就是把 From trait 倒过来而已。也就是说,如果你为你的类型实现了 From,那么同时你也就免费获得了 Into。
使用 Into trait 通常要求指明要转换到的类型,因为编译器大多数时候不能推断它。不过考虑到我们免费获得了 Into,这点代价不值一提。

let spend = 3;
let my_spend: MyNumber = spend.into();
println!("{:?}", my_spend);

解析字符串

经常需要把字符串转成数字。完成这项工作的标准手段是用 parse 函数。

只要对目标类型实现了 FromStr trait,就可以用 parse 把字符串转换成目标类型。 标准库中已经给无数种类型实现了 FromStr。如果要转换到用户定义类型,只要手动实现 FromStr 就行。

let cost: i32 = "5".parse().unwrap();
println!("{}", cost);

match

解构指针和引用

对指针来说,解构(destructure)和解引用(dereference)要区分开,因为这两者的概念是不同的,和 C 那样的语言用法不一样。

  • 解引用使用 *
  • 解构使用 &、ref、和 ref mut

```rs
// 获得一个 `i32` 类型的引用。`&` 表示取引用。
    let num = &100;

    match num {
        // 用 `&val` 这个模式去匹配 `num`
        &val => println!("&val 是: {:?}", val),
    }

    // 如果不想用 `&`,需要在匹配前解引用。
    match *num {
        val => println!("val 是: {:?}", val),
    }

    // Rust 对这种情况提供了 `ref`。它更改了赋值行为,从而可以对具体值创建引用。
    // 下面这行将得到一个引用。
    let ref num3 = 66;

    // 相应地,定义两个非引用的变量,通过 `ref` 和 `ref mut` 仍可取得其引用。
    let num4 = 5;
    let mut mut_num4 = 7;

    // 使用 `ref` 关键字来创建引用。
    // 下面的 r 是 `&i32` 类型,它像 `i32` 一样可以直接打印,因此用法上
    // 似乎看不出什么区别。但读者可以把 `println!` 中的 `r` 改成 `*r`,仍然能
    // 正常运行。前面例子中的 `println!` 里就不能是 `*val`,因为不能对整数解
    // 引用。
    match num4 {
        ref r => println!("num4 r is: {:?}", r),
    }

    // 类似地使用 `ref mut`。
    match mut_num4 {
        ref mut m => {
            // 已经获得了 `mut_value` 的引用,先要解引用,才能改变它的值。
            *m += 10;
            println!("`mut_value`: {:?}", m);
        }
    }

解构结构体

struct Study {
    name: String,
    target: String,
    spend: u32,
}
fn main(){

let s = Study {
        name: String::from("从0到Go语言微服务架构师"),
        target: String::from("全面掌握Go语言微服务落地,代码级一次性解决微服务和分布式系统。"),
        spend: 3,
};

let Study {
        name: name,
        target: target,
        spend: spend,
} = s;

println!("name = {}, target = {},  spend = {} ", name, target, spend);
// name = 从0到Go语言微服务架构师, target = 全面掌握Go语言微服务落地,代码级一次性解决微服务和分布式系统。,  spend = 3
let s2 = Study {
        name: String::from("《Go语言极简一本通》"),
        target: String::from("学习Go语言,并且完成一个单体服务。"),
        spend: 5,
};
// 也可以忽略某些变量
let Study { name, .. } = s2;
println!("name = {}", name);
//name = 《Go语言极简一本通》
}

async和await

[dependencies]
async-std = {version = "1.12.0", features = ["attributes"]}

多线程提高执行效率

use async_std::task::{sleep, spawn};
use std::time::Duration;

async fn do3() {
    for i in 1..=5 {
        println!("do3 {}", i);
        sleep(Duration::from_millis(500)).await;
    }
}

async fn do4() {
    for i in 1..=5 {
        println!("do4 {}", i);
        sleep(Duration::from_millis(1000)).await;
    }
}

#[async_std::main]
async fn main() {
    let do3_async = spawn(do3());
    do4().await;
    do3_async.await;
}

async 关键字的作用

  1. 可以在函数体内使用.await 语法。
  2. 它修改了函数的返回类型。
    async fn 函数名称() -> 返回值类型 实际上返回的是 impl std::future::Future<Output=返回值类型>
  3. 自动将结果值封装进一个新的 Future 对象。
async fn lesson() -> String {
    String::from("lesson")
}

async fn join_lesson() -> impl Future<Output = String> {
     async {
        let lesson = lesson().await;
        "lesson".to_string() + &lesson
    }
}

async fn join_lesson2() -> impl Future<Output = String> {
    let r = |x: String| async move {
        let y: String = join_lesson().await.await;
        y + &*x
    };
    r(String::from(":12131312"))
}
...
    let result = block_on(join_lesson2()).await;
    println!("{}", result);