• Rust 语法笔记


    变量绑定(声明变量)

    let 变量名: 类型 = 变量值;
    let 变量名 = 变量值[类型];
    // 整型 默认 i32;浮点 默认 f64
    
    • 1
    • 2
    • 3

    所有的 let 绑定都必须尾接;代码块也不例外。

    mut

    可以通过重新声明的方式来改变变量类型
    可以下划线改善数字的可读性

    声明常量
    const / static
    除了string字面量,其他类型的 static 必须显示声明类型 &'static str


    原生类型 primitives

    标量类型 scalar type

    * 有符号整数(signed integers)

    i8i16i32i64i128isize(指针宽度)

    * 无符号整数(unsigned integers)

    u8u16u32u64u128usize(指针宽度)

    * 浮点数(floating point)

    f32f64

    * 字符(char)

    char 单个 Unicode 字符,如 ‘a’,‘α’ 和 ‘∞’(每个都是 4 字节)

    * 布尔型(bool)

    bool 只能是 truefalse

    * 单元类型(unit type)

    ()。其唯一可能的值就是 () 这个空元组

    尽管单元类型的值是个元组,它却并不被认为是复合类型,因为并不包含多个值。

    复合类型 compound type

    数组(array)

    如 [1, 2, 3]

    类型标记 [类型; 长度]

    切片 slice
    长度不定
    类型标记 &[T]
    slice 可以用来借用数组的一部分

    slice[0]
    slice.len()
    
    • 1
    • 2

    数组可以自动被借用成为 slice &数组名

    元组(tuple)

    如 (1, true)
    元组可以解构赋值

    let foo = Foo { x: (1, 2), y: 3 };
    let Foo { x: (a, b), y } = foo;
    
    • 1
    • 2

    可以通过下标访问 元组名.0

    单个元素的元组需要补一个逗号, 与带括号的字面量区分开

    元组可以嵌套

    函数可以使用元组来返回多个值

    自定义类型

    结构体 struct

    • 元组结构体 相当于 具名元组
    • C 语言风格结构体
    struct 结构名 {
        属性名1: 类型,
        属性名2: 类型,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 单元结构体(unit struct)
      不带字段,在泛型中很有用

    .. 解构结构体只会添加还没有设置的元素

    let point: Point = Point { x: 10.3, y: 0.4 };
    let bottom_right = Point { x: 5.2, ..point };
    // (5.2, 0.4)
    
    let Point { x: left_edge, y: top_edge } = point;
    // left_edge top_edge 分别取到 x,y 的值
    let Pair(integer, decimal) = Pair(1, 0.1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    枚举 enum

    enum WebEvent {
        // 一个 `enum` 可以是单元结构体(称为 `unit-like` 或 `unit`),
        PageLoad,
        PageUnload,
        // 或者一个元组结构体,
        KeyPress(char),
        Paste(String),
        // 或者一个普通的结构体。
        Click { x: i64, y: i64 }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    访问枚举值

    // 方法一:
    WebEvent::PageLoad
    
    // 方法二:
    use WebEvent::{PageLoad};
    // or
    // use WebEvent::*;
    let xxx = PageLoad; // 等价于 WebEvent::PageLoad
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    分支判断枚举

    match event {
        WebEvent::PageLoad => println!("page loaded"),
        WebEvent::PageUnload => println!("page unloaded"),
        // 从 `enum` 里解构出 `c`。
        WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
        WebEvent::Paste(s) => println!("pasted \"{}\".", s),
        // 把 `Click` 解构给 `x` and `y`。
        WebEvent::Click { x, y } => {
            println!("clicked at x={}, y={}.", x, y);
        },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    枚举默认值从0开始
    显示赋值

    enum Color {
        Red = 0xff0000,
        Green = 0x00ff00,
        Blue = 0x0000ff,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用时可进行类型转化来访问值

    Color::Red as i32
    
    • 1

    enum 的一个常见用法就是创建链表


    类型系统

    类型转换

    Rust 不提供原生类型之间的隐式类型转换

    通过 as 显示的类型转化

    someVar as u32
    
    • 1

    当把任何类型转换为无符号类型 T 时(数据范围不匹配),会不断加上或减去 (std::T::MAX + 1)直到值位于新类型 T 的范围内。
    实际实现是:从最低有效位(LSB,least significant bits)开始保留 8 位,然后剩余位置,直到最高有效位(MSB,most significant bit)都被抛弃。
    当把无符号类型转化为等长的有符号类型,最高位为1时标记为负数详情查阅 计算机原理-补码相关内容

    #![allow(overflowing_literals)] 不显示类型转换产生的溢出警告。

    rust 1.45 以后,将浮点数转化为无符号整数,超出上限 会直接转化为最大值;低于下限 会直接取 0。
    因为若按上述方法转化会让结果难以预料。
    但依然可以使用 .to_int_unchecked::() 维持原来的转化方式

    字面量

    可通过后缀方式声明其类型
    整数 默认 u32
    浮点数 默认 f64

    类型推断

    可以根据赋予的值,来推断类型
    减少显示声明类型

    Vec 可以通过传入数据的类型 确定其类型

    别名 type

    可以使用 type 对类型进行别名。
    但必须采用大驼峰的命名方式

    type Inch = u64;
    
    • 1

    可以使用 #[allow(non_camel_case_types)] 屏蔽此规则

    类型转化方法

    最一般的转换会用到 From 和 Into 两个 trait。

    From 与 Into

    impl From<i32> for Number {
        fn from(item: i32) -> Self {
            Number { value: item }
        }
    }
    let num = Number::from(30);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Into trait 就是把 From trait 倒过来而已

    已经写 From 后,便不再需要写 Into 了
    同into的类型也不需要注明

    let int = 5;
    let num: Number = int.into();
    
    • 1
    • 2

    TryFrom 与 TryInto

    use std::convert::TryFrom;
    use std::convert::TryInto;
    
    • 1
    • 2

    TryFrom 和 TryInto trait 用于易出错的转换,也正因如此,其返回值是 Result 型。

    impl TryFrom<i32> for EvenNumber {
        type Error = ();
    
        fn try_from(value: i32) -> Result<Self, Self::Error> {
            if value % 2 == 0 {
                Ok(EvenNumber(value))
            } else {
                Err(())
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Ok()
    Err()

    let result: Result<EvenNumber, ()> = EvenNumber::try_from(8)
    let result: Result<EvenNumber, ()> = 8i32.try_into();
    
    • 1
    • 2

    ToString 与 FromStr

    实现 fmt::Display trait,它会自动提供 ToString

    调用 ToString

    circle.to_string()
    
    • 1
    use std::string::ToString;
    impl ToString for Circle {
        fn to_string(&self) -> String {
            format!("Circle of radius {:?}", self.radius)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    只要对目标类型实现了 FromStr trait,就可以用 parse 把字符串转换成目标类型。

    // 两种提供类型的方式
    let parsed: i32 = "5".parse().unwrap();
    let turbo_parsed = "10".parse::<i32>().unwrap();
    
    • 1
    • 2
    • 3

    表达式

    代码块也是表达式,所以它们可以用作赋值中的值。

    代码块中实际执行的 最后一个表达式 将作为代码块的返回
    注意:不要加分号。加了分号就是普通语句,最会代码块中就没有执行的表达式,因而会返回()

    流程控制

    if/else

    条件不需要用括号包裹

    if n < 0 {
        print!("{} is negative", n);
    } else if n > 0 {
        print!("{} is positive", n);
    } else {
        print!("{} is zero", n);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    if else 本质上也是代码块,因此也可以用于赋值

    loop

    loop 无限循环

    loop {
    	···
        if 条件 {
            // 跳过这次迭代的剩下内容
            continue;
        }
        if 条件 {
            // 退出循环
            break;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    循环设置标签
    continue、break 可以通过标签 直接影响外层循环

    'outer: loop {
    	'inner: loop {
    		break 'outer;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以通过break 表达式; 为 loop 设置返回值。

    用途:尝试一个操作直到成功为止

    while

    while 条件 {
    }
    
    • 1
    • 2

    for

    使用区间标记 a..b 可以创建一个迭代器
    a..=b 包含b

    for n in 1..101 {
    }
    
    • 1
    • 2

    for 循环默认会使用 into_iter 函数

    for name in names.iter()
    for name in names.into_iter()
    for name in names.iter_mut()
    
    • 1
    • 2
    • 3

    迭代器的方法 into_iteriter iter_mut
    iter - 在每次迭代中借用集合中的一个元素。这样集合本身不会被改变,循环之后仍可以使用。
    into_iter - 会消耗集合。在每次迭代中,集合中的数据本身会被提供。一旦集合被消耗了,之后就无法再使用了,因为它已经在循环中被 “移除”(move)了。
    iter_mut - 可变地(mutably)借用集合中的每个元素,从而允许集合被就地修改。

    match

    match 会检查匹配覆盖

    match number {
       // 匹配单个值
       1 => println!("One!"),
       // 匹配多个值
       2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
       // 试一试 ^ 将 13 添加到质数列表中
       // 匹配一个闭区间范围
       13..=19 => println!("A teen"),
       // 处理其他情况
       _ => println!("Ain't special"),
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    match 解构方式

    解构元组

    match triple {
        // 解构出第二个和第三个元素
        (0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z),
        // `..` 可用来忽略元组的其余部分
        (1, ..)  => println!("First is `1` and the rest doesn't matter"),
        _      => println!("It doesn't matter what they are"),
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    解构枚举
    枚举中的元组也可通过上法解构

    解构指针
    现在还不懂指针,先跳过???

    match 卫语句(guard)

    可以加上 match 卫语句(guard) 来过滤分支。
    给匹配增加额外的if条件判断

    match pair {
    	// “if x == y” 是一个卫语句
        (x, y) if x == y => println!("These are twins"),
        (x, y) if x + y == 0 => println!("Antimatter, kaboom!"),
        (x, _) if x % 2 == 1 => println!("The first one is odd"),
        _ => println!("No correlation..."),
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    match 重新绑定

    在 match 中,若间接地访问一个变量,则不经过重新绑定就无法在分支中再使用它。
    @ 符号 用来绑定变量到名称

    match age {
      n @ 1  ..= 12 => println!("I'm a child of age {:?}", n),
    }
    
    • 1
    • 2
    • 3

    if let

    判断let是否绑定成功

    // 若 `let` 将 `number` 解构成 `Some(i)`,则执行
    if let Some(i) = number {
      println!("Matched {:?}!", i);
    }
    
    // 匹配枚举
    if let Foo::Bar = b {
        println!("b is foobar");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    直接使用 if Foo::Bar==a,需要注明 #[derive(PartialEq)]

    while let

    与上类似
    可以简化 循环与match的组合代码

    函数

    默认与代码块的返回逻辑相同
    但可以 return 提前返回

    fn 函数名(参数: 类型, ···) -> 返回类型 () {
    	···
    	[return xxx]
    }
    
    • 1
    • 2
    • 3
    • 4

    方法

    依附于对象的函数

    方法在 impl 代码块中定义。
    通过关键字 self 来访问对象中的数据和其他。

    selfself: Self 的语法糖(sugar)其中 Self 是方法调用者的类型。
    &selfself: &Self 的语法糖
    &mut selfself: &mut Self

    self: 会消耗本身
    &self: 引用 self
    &mut self: 可变引用 self
    加入到方法的参数中

    impl Point {
        fn origin() -> Point {
            Point { x: 0.0, y: 0.0 }
        }
        fn new(x: f64, y: f64) -> Point {
            Point { x: x, y: y }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    闭包

    Rust 闭包 closure 也叫做 拉姆达表达式 lambda

    它们的语法和能力使它们在临时(on the fly)使用时相当方便。

    • 声明时使用 || 替代 () 将输入参数括起来。
    • 函数体定界符({})对于单个表达式是可选的。
    • 有能力捕获外部环境的变量。
    |i: i32| -> i32 { i + 1 };
    |i     |          i + 1  ;
    
    • 1
    • 2
  • 相关阅读:
    微服务项目:尚融宝(38)(核心业务流程:申请借款额度(1))
    pycharm 用MD文件制作流程图
    深度学习入门(6) - 3DV 三维视觉
    移动Web:媒体查询及手机端PC端识别
    指针类型的意义
    RabbitMQ快速上手以及RabbitMQ交换机的四种模式
    《Tree of Thoughts: Deliberate Problem Solving with Large Language Models》中文翻译
    C++面向对象语言自制多级菜单
    Android流媒体开发之路三:基于NDK开发Android平台RTSP播放器
    网工知识角|华为网络工程师,华为、华三、思科设备三层交换机如何使用三层接口?命令敲起来
  • 原文地址:https://blog.csdn.net/I_fole_you/article/details/133916831