IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Rust 手册

    haofly发表于 2022-03-29 14:55:00
    love 0

    安装配置

    • Cargo是它的包管理工具,类似于npm,可以在这里搜索包crates.io
    • 安装完成后cargo, rustc, rustup工具会在~/.cargo/bin中,可以讲他们加入到环境变量中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh# 安装rust及对应的工具链

    cargo new my_project --bin# 新建项目,如果是二进制程序就--bin,如果是创建库就--lib
    cargo install --locked --path .# 根据Cargo.lock安装,相当于npm install

    cargo build# 编译
    cargo build --release # 进行一些优化进行编译,相当于npm run production
    ./target/debug/my_project# 运行编译后的二进制文件
    cargo run#

    # 如果出现error E0554 may not be used on the stable release channel的错误,需要使用nightly模式来安装与性能了
    rustup install nightly
    cargo +nightly run ...
    cargo +nightly install ...
    rustup install nightly-2022-03-22# 安装指定版本的nightly
    cargo +nightly-2022-03-22 run ...# 使用指定版本的nightly
    • Cargo.toml:类似于package.json文件
    1
    2
    3
    4
    5
    6
    [package]
    name = "my_project"
    version = "0.0.0.0"

    [dependencies]# 这里可以添加依赖项
    dotenv = "0.15.0"

    基本语法

    • 数据类型
      • bool、u8、u16、u32、u64、u128、usize、i8、i16、i32、i64、i128、isize、f32、f64、char
      • Tuple、Array
    • Option: 一种可能为空的值,本质上也是枚举,None表示空,Some代表又值。(好处是能够在编译阶段就能阻止可能发生的错误),拿它的值最好的方法就是if let。代码量和我们平时写的if (val !== null)一样,只是这相当于是强制的了
    • Result: 结果,本质也是枚举,Ok表示正常返回值和Err表示异常
    • ?: 如果一个函数内部有多个可能会抛出异常的地方,可以直接在结尾使用?表示只要抛错,那么函数返回值就是Err,而不用使用unwrap或者if let了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    println!("{} days", day); // 打印变量的值

    // Option
    obj.field.is_some()// 判断某个option的字段是否有值
    let val: Option<u16> = get_val()// 获取一个可能为空的u16的值
    if let Some(var1) = val {
    println!("val is: {}", var1)
    } else {
    println!("val is None")
    }
    let var1: u16 = val.unwrap() // 如果不想写if let,那么可以直接这样取值,但是如果值为空,会直接报错panic


    // Result,实现了FromIter的,可以使用一些迭代器的方法
    let res: Result<u16, &str> = Ok(233)// 这里定义res是一个Result类型,正常返回u16,如果报错则是一个字符串
    if let Ok(var) = res {}
    let val: u16 = res.unwrap()// 和Option的unwrap一样,直接取正常返回值,错误直接panic报错
    res.is_ok()// 该结果是否是ok的,常用语函数返回Ok(result),可以这样判断
    res.map_err(|err| err.to_string())// 处理错误
    res.map_err(|_| println!("error"))// 如果不关心具体某个错误就用_
    res.filter_map(|r| r.parse::<i32>().ok())// 过滤结果

    基本变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    // 数字
    let a = 123i32;// 定义一个i32的数字类型
    let b = 123_i32;// 同上
    std::i32::MAX// 获取某种类型的最大值
    10_i8 as u16// 隐式转换
    3_u8.pow(2)// 计算平方
    3_i32.abs()// 计算绝对值

    // 布尔
    if x {}

    // 字符串, str是String的切片类型,是String的一部分或全部
    let s = "abc"// s的类型为&str
    let s = String::from("abc")// String类型
    let s = "abc".to_string()// 转换为String类型
    s.push("a")// push追加单个字符
    s.push_str("abc")// 追加一个字符串


    // Tuple
    let n = (1, 2, 3)
    n.0
    n.1
    let (name, age): ($str, i32) = ("abc", 123);// 居然还能类型推导

    // Array,长度是固定的,不能动态增减,向量才可以做到
    let arr1 = [11, 22, 33];
    let arr1: [&str; 3] = ["aa", "bb", "cc"]// 指定数组的长度
    arr1.len()// 获取数组长度
    for i in arr1.iter() {} // 遍历数组
    for i in &arr1() {}// 遍历数组2
    let s1 = &arr1[0..3];// 取数组前两个元素
    let s1 = &arr1[1..=3];// 取2到4的位置的元素
    let s1 = &arr[..];// 取所有元素


    // 格式转换
    my_int.to_string();// int转字符串
    my_str.as_bytes();// 字符串转bytecode
    my_str.parse::<i32>().unwrap();// 字符串转int
    let a: i32 = my_str.parse().unwrap()// 字符串转数字
    Optional::from();// 将指定的s变量转换为Option<T>的形式

    // JSON格式字符串
    use serde_json::Value;// serde_json = "1.0.57"
    let my_json: Value = serde_json::from_str("")?;
    let field_value = my_json["field"}.as_str();
    Ok(json!(res))// 将结果转换为json格式

    扩展变量

    Vec向量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    let mut v1 = Vec::new();
    let mut v1 = Vec::with_capacity(10);// 指定容量
    let v2 = vec![1,2,3]
    let v3 = vec!(2;3)// 即vec![2,2,2]
    assert_eq!(v1, [1]); // 可以和数组进行比较
    v[n]; // 获取第几个元素
    v.get(n).unwrap();// 取第n个元素,不存在则报错
    for i in v {}// 遍历vec
    v.len()// 获取长度
    v.is_empty()// 是否为空
    v.push(1)// 在向量尾部增加元素
    v.pop()// 从尾部去掉元素
    v.contains(&"abc")// 是否包含
    v.insert(1, 222)// 在位置1插入元素
    v.remove(1)// 删除置顶位置的元素
    v.clear()// 晴空
    v.append(v1)// 将另一个vec合并到v中
    v.truncate(2)// 截断,保留n个与阿奴
    v.retain(|x| *x > 20) // 只保留满足条件的元素,相当于filter
    v.drain(1..=3)// 删除并返回指定范围的元素
    v.split_off(2); // 删除并返回前n个元素

    if !v1.is_empty() {
    let first = v1[0] // 获取第一个元素
    }

    Struct结构体(类)

    • 加上Debug这个trait就能轻松打印了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // Struct结构体(有点像类)
    #[derive(Debug)]// 只要加上这一行就能直接用println打印,println("{:?}", user)或者println("{:#?}", user)
    struct User {
    name: String,
    age: u32,
    email: String
    }
    let email = String::from("a@b.com")
    let user = User {
    name: String::from("ac"),
    age: 27,
    email,// 居然也能和js一样简写
    }
    let user2 = User {
    name: String::from("ab"),
    ...user1// 但是这种简写方式只适用于可以Copy的变量,不能Copy的变量例如String是不行的
    ...user1.clone()// 即使是不能copy的也可以直接用
    }
    user.name // 访问结构体的字段

    struct Color(i32, i32, i32); // 没有字段名的结构体,常用语比较简单的情况
    let a = Color(0, 0, 0)
    // 给结构体增加方法
    impl User {
    fn func1(&self) -> i32 {
    self.age + 10
    }
    }
    user1.func1()// 这样就能直接调用了,有点儿像类了

    // fmt::Debug的输出不够自观,可以自己实现fmt::Display来打印更加直观的数据
    impl fmt::Display for MyStructure {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{}", self.0)
    }
    }

    Enum枚举

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    enum Gender {
    Male,
    Female
    }
    impl Gender {
    fn isMale(&self) -> bool {
    return *self as u8
    }
    }
    let a: Gender = Gender::Male;
    let b = Gender::Female as i32;// 1

    Trait(抽象类)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    triat Playable {
    fn play(&self);
    fn pause(&self) {
    println!("pause")
    }
    fn get_duration(&self) -> f32
    }
    struct Audio {name: String, duration: f32}
    impl Playble for Audio {// 对,用结构体来实现
    fn play(&self) {}
    fn get_duration(&self) -> f32 {
    self.duration
    }
    }

    Iterator迭代器

    1
    2
    3
    4
    5
    .map(|x| x + 1)// 这就和js中的map类似了
    .next()// 取下一个元素,一定是一个Option<T>的类型
    .collect() // 将迭代器的元素收集到指定的类型中
    let val2: Vec<_> = val1.iter().collect();// 这里的<_>表示不指定类型,因为编译器能自动推导
    .collect::<Vec<Document>>()// 转换为指定的类型

    BSON序列化和反序列化

    • 很多类型的变量都能表示为BSON值
    1
    2
    bson::from_bson()// 序列化
    bson::from_document()// 反序列化

    流程控制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    for i in 1..5 {}// 生成1到4的整数
    for i in 1..=5 {}// 生成1到5的整数

    // if 语句
    if COND1 {} else if COND2 {} else {}// if...else if...else
    // 如果分支最后一行没有分号结尾,那么表达式的值就为那一行的结果
    // 每个分支的返回值的类型必须相同
    // 可以每个分支都不返回结果,那么返回值为(),如果其中有一个返回了结果,那么必须有else语句,否则会报错
    let a = if x < 10 {
    x + 10
    } else {
    x
    }

    // while循环
    while x < 5 {
    break
    continue
    }

    // loop循环,相当于没有条件的while,只能用break来终止
    loop {
    break;
    }

    文件操作

    1
    let my_str = include_str!("filename");// 将文件内容读取为字符串

    测试

    • 可以在每个文件的下面编写针对当前文件的测试,他们只有在cargo test的时候才会运行,在cargo build的时候不会
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #[cfg(test)]
    mod tests {
    #[test]
    fn test1() {
    assert_eq!(2 + 2, 4);
    }

    #[test]
    fn test2() {
    panic!("fail");
    }
    }

    TroubleShooting

    • failed to run custom build command for openssl-sys v0.9.66: 执行sudo apt install libssl-dev pkg-config -y
    • **type ascription is experimental **: 在使用某些实验方法的时候可能会有这个错误,此时只需要将#![feature(type_ascription)]放到整个项目入口文件的开头即可main.rs或者lib.rs
    • error: no rules expected the token aarch64_apple: 目前我仅在2022-03-22后的几个版本遇到过这个问题,安装rustup install nightly-2022-03-22版本可以解决(注意使用的时候也需要指定版本cargo +nightly-2022-03-22),当然如果最新的修复了,可以尝试一下最新的版本
    • use of moved value: 通常是因为值饮用造成的,我们需要直接用指针来使用&var


沪ICP备19023445号-2号
友情链接