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

    Rust tips #1 ~ #20

    smallnest发表于 2024-06-06 01:27:44
    love 0

    Tip #001

    Rust 不支持静态 vec(static vec),但是最接近的是静态数组。例如,如果你想存储三个字符串的数组,可以尝试这样: static STRINGS : [&str;3] = ["a", "b", "c"]

    Tip #002

    什么是可选值(optional)和 unwrap()? 可以将可选值想象成一个信封,它可以包含一个值(Some(item))或者什么都没有(None)。对可选值调用 unwrap() 要么返回包含的值,要么如果可选值是 None 的话就会使程序panic。

    Tip #003

    关于可选值( optional)的安全解包方式:

    • 使用 match 语句明确处理不同情况
    • unwrap_or_default: 要么解包得到值,要么返回默认值
    • unwrap_or_else: 允许你指定一个函数来处理 None/Error 解包结果

    Tip #004

    如果你没有时间完成特定的一段代码,但仍然希望程序可以编译,可以考虑使用 todo!() 或 unimplemented!() 宏。你的代码会继续编译通过,但如果程序运行到包含这些宏的代码块中,它将会panic。

    todo! 更适合临时标记,而 unimplemented! 则更适合长期未实现的情况。

    Tip #005

    如果你想测试一个枚举类型的实例是否符合枚举的特定变体,你可以使用 matches! 宏,例如:
    let match_res = matches!(my_variable, enum_type);

    你也可以匹配其他模式,如范围,例如: matches!(foo, 'A'..='Z')

    Tip #006

    你知道吗, {} 块可以像函数一样返回结果?这使得基于条件的赋值变得非常容易。例如:

    1
    2
    3
    4
    5
    6
    7
    let car_ready = {
    start_engine();
    match engine_state {
    Engine::running => true,
    Engine::error => false
    }
    }

    Tip #007

    const和static之间有什么区别?

    • const值在编译期间会被替换("内联")到你的代码中。这对于在代码的各个地方使用的常量值来说是理想的。
    • static值在运行时有一个内存地址,并且会在整个应用程序的生命周期中存在。它们可以是可变的。如果你需要一个固定的位置来存放共享资源,例如硬件驱动程序或数据库接口,那么静态变量就很有用。

    Tip #008

    如果你想将几个相同或不同类型的值一起存储,元组类型就很有用。下面是一个将元组类型声明为结构体元组的示例,访问元组类型字段,以及从函数返回"匿名"元组类型:

    1
    2
    3
    4
    5
    6
    struct MyTuple(String, i32);
    fn flip(a: (i32, String)) -> MyTuple {
    let my_tuple = MyTuple(String::from("the answer"), 42);
    (a.1, a.0)
    }

    Tips #009

    让我们来谈谈 string

    在 Rust 中有两种基本的字符串类型: String 和 str。

    String: (也称为 Owned String),在堆上分配内存并且可变。String 在运行时使用,当你想要创建和修改字符串时。你可以将 String 作为 &str 引用传递给只读函数。

    str: (也称为 String Slice) 是对一序列 UTF8 数据的引用。你可以在编译时以常量、静态字面量的形式创建 str,或者在运行时从 String 对象获取它们。str 总是不可变的。

    Tips #010

    拼接 string 有两种方式可以将两个字符串连接,分别使用:

    • push_str
    • format! 宏
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 使用 push_str
    let mut first = String::from("Hello, ");
    first.push_str("world!");
    assert_eq!(first, "Hello, world!");
    // 使用 format! 宏
    let second = String::from("Hello, ");
    let combined = format!("{}{}", second, "world!");
    assert_eq!(combined, "Hello, world!");

    Tips #011

    格式化打印宏

    • print!、println!: 在控制台打印文本。
      您可以使用格式字符串打印变量的内容,例如, let a: i32 = 1234; println!("The value of a is: {}", a); 将打印出 "The value of a is 1234"
    • eprintln!: 打印到标准错误流(stderr)
    • dbg!: 打印变量的值和行号,对于轻量级调试很有用。

    Tips #012

    可选值解包使用 if let() 类似于 Swift , Rust 允许我们使用 if let 来测试一个可选值是否有值,这种方式便于保持程序流程的简洁:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    fn main() {
    let optional_value: Option<i32> = Some(9);
    // 传统方式
    match optional_value {
    Some(value) => println!("The value is: {}", value),
    None => println!("There is no value"),
    }
    // 使用 if let
    if let Some(value) = optional_value {
    println!("The value is: {}", value);
    } else {
    println!("There is no value");
    }
    }

    Tips #013

    使用 if let 解包多个可选值。继上一条 tip,如果你需要同时检查多个可选值是否都有值,你可以使用 if let 来测试和解包可选值的元组:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    fn main() {
    let optional_tuple: (Option<i32>, Option<bool>) = (Some(5), Some(true));
    // 传统方式
    match optional_tuple {
    (Some(int_value), Some(bool_value)) => {
    println!("Received int: {} and bool: {}", int_value, bool_value);
    }
    _ => println!("One or more options were None"),
    }
    // 使用 if let
    if let (Some(int_value), Some(bool_value)) = optional_tuple {
    println!("Received int: {} and bool: {}", int_value, bool_value);
    } else {
    println!("One or more options were None");
    }
    }

    Tips #014

    实例化 vector。
    Vec是一种动态数组类型,非常适合存储相同数据类型的序列。下面是一些使用标准库调用和vec!宏实例化Vec的方式。注意类型是如何处理的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Type automatically inferred by the next push.
    let mut a: Vec<i64> = Vec::new();
    a.push(1_i64);
    // Type set during instantiation.
    let mut b: Vec<i64> = Vec::<i64>::new();
    // Type defined explicitly, initialized using vec!
    let mut c: Vec<i64> = vec![];
    // Type automatically inferred, prefilled with vec!
    let mut d: Vec<i64> = vec![1_i64, 2_i64, 3_i64];
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 空Vec
    let mut vec1: Vec<i32> = Vec::new();
    // 用值初始化
    let mut vec2 = vec![1, 2, 3];
    // 用指定数量的元素和默认值初始化
    let mut vec3 = vec![0; 5]; // vec3 = [0, 0, 0, 0, 0]
    // 从迭代器获取元素
    let vec4 = (0..5).collect();
    let vec5: Vec<_> = ["foo", "bar"].into_iter().collect();
    // 用给定的初始容量初始化
    let mut vec6 = Vec::with_capacity(10);

    Tip #015

    Vector迭代。

    你可以使用 vec.iter() 来对 Vector进行迭代,使用 map() 或 for_each() 函数,这与例如 JavaScript 中的方式类似。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let numbers: Vec<i32> = vec![1, 2, 3, 4, 5];
    // Using for_each to print each element
    numbers.iter().for_each(|&x: i32| {
    println!("{}", x);
    });
    // Use map to transform a vector.
    let squared: Vec<i32> = numbers.iter().map(|&x: i32| x * x).collect();
    println!("{:?}", squared);

    Tips #016

    嘿,你有一些不错的HashMap,怎么初始化它们?除了创建一个新的HashMap并插入键值对之外,一种值得注意的方式是使用一个元组数组和一个#迭代器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    use std::collections::HashMap;
    fn main() {
    // 从一个元组数组创建HashMap
    let codes: HashMap<_, _> = [
    (101, "abc"),
    (102, "def"),
    (103, "ghi"),
    ].iter().cloned().collect();
    println!("codes = {:?}", codes);
    }

    Tips #017

    更好的方式来unwrap()
    如果内容是None的话,unwrap()一个可选值会导致panic。以下是在unwrap时处理None的一些方法:

    • unwrap_or_default(): 如果可选值是None,返回默认值。
    • unwrap_or(): 提供一个替代值。
      -unwrap_or_else(): 执行一个函数,如果可选值是None的话该函数返回某个值。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    fn main() {
    let x: Option<i32> = None;
    // 返回默认值0
    println!("{}", x.unwrap_or_default());
    // 返回提供的替代值99
    println!("{}", x.unwrap_or(99));
    // 返回函数提供的值
    println!("{}", x.unwrap_or_else(|| "No value".to_string()));
    }

    Tips #018

    Rust 允许我们轻松定义匿名函数(也称为lambda 或 closure 或闭包)。闭包通常与 iterator 一起使用,或者用于定义回调函数。以下是语法示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    fn main() {
    let multiply = |a, b| a * b;
    let product = multiply(3, 4);
    println!("Product: {}", product); // 输出: Product: 12
    let add_two = |x| x + 2;
    let values = vec![1, 2, 3, 4, 5];
    let new_values: Vec<_> = values.iter().map(add_two).collect();
    println!("New values: {:?}", new_values); // 输出: New values: [3, 4, 5, 6, 7]
    }

    Tips #019

    在闭包中使用 move 可以从周围作用域捕获变量。这意味着闭包将获取这些变量的所有权,而无需传递任何参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    fn main() {
    let num = 5;
    let closure = || {
    println!("Num: {}", num); // 错误: `num` 不可借用
    };
    let closure_move = move || {
    println!("Num: {}", num); // 正确: `num` 被移动到闭包中
    };
    closure(); // 调用没有捕获变量的闭包
    closure_move(); // 调用捕获了 `num` 的闭包
    }

    Tips #020

    实现 From() trait 提供了一种在复杂类型之间进行转换的机制。Rust 还会自动为对应的类型提供一个 Into() 实现。在下面的示例中,我们为 ColorFloat 实现了 From,但是可以使用 Color8Bit 的 Into():

    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
    struct Color8Bit {
    r: u8,
    g: u8,
    b: u8,
    }
    struct ColorFloat {
    r: f32,
    g: f32,
    b: f32,
    }
    impl From<Color8Bit> for ColorFloat {
    fn from(color: Color8Bit) -> Self {
    ColorFloat {
    r: (color.r as f32) / 255.0,
    g: (color.g as f32) / 255.0,
    b: (color.b as f32) / 255.0,
    }
    }
    }
    fn main() {
    let color8 = Color8Bit { r: 230, g: 100, b: 50 };
    // 使用 Into 特性将 Color8Bit 转换为 ColorFloat
    let color_float: ColorFloat = color8.into();
    println!("Color Float: {}, {}, {}", color_float.r, color_float.g, color_float.b);
    }

    整理自 Daily Rust @rustoftheday



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