变量和 mutable

let:声明变量

  1. let x = 5;
  2. let x: i32 = 5;
  3. // 变量需要被修改就用mut修饰
  4. let mut x = 5
  5. x = 6;

mut:可变性

const:声明常量

  1. // 常量必须声明类型
  2. const A_CONST: i32 = 1;

shadowing: 重复声明变量会隐藏前面的

  1. // 隐藏写法(更符合rust思想)
  2. let x = 5;
  3. let x = x + 1;
  4. let x = x * 2;
  5. println!("x is {}", x); // x is 12
  6. // mutable写法
  7. let mut x = 5;
  8. x = x + 1;
  9. x = x * 2;
  10. println!("x is {}", x); // x is 12

基础数据类型

Rust 是静态,强类型语言,所有变量的类型必须在编译期就明确固定

整数

  • 12 种整数类型 | 长度 | 有符号 | 无符号 | | —- | —- | —- | | 8 bit | i8 | u8 | | 16 bit | i16 | u16 | | 32 bit | i32 | u32 | | 64 bit | i64 | u64 | | 128 bit | i128 | u128 | | arch | isize | usize |

  • 对于未明确标注类型的整数,Rust 默认采用 i32

  • isize和usize根据系统的不同而有不同长度

    溢出

  • 计算结果大于寄存器或者存储器所能存储或表示的范围

  • 编程语言对待溢出的方法:
    • crash:程序直接退出
    • ignore:最普遍的做法,忽略任何算数溢出
  • Rust debug 模式下编译会检查整数溢出并 crash,release 模式不会检查溢出
    • cargo build 默认 debug 模式 ,可以加 —release 切换到 release 模式
  • 要显式处理溢出,可以适用标准库提供的.overflowing_*系列方法,比如:overflowing_add ```rust // 需要处理整数溢出 fn avg(a: u32, b: u32) -> u32 { // Hacker-sDelight 中的技巧 (a & b) + ((a ^ b) >> 1) }

fn main() { assert_eq!(avg(4294967295, 4294967295), 4294967295); assert_eq!(avg(0, 0), 0); assert_eq!(avg(10, 20), 15); assert_eq!(avg(4294967295, 1), 2147483648); println!(“passed”) }

  1. <a name="qeZ0a"></a>
  2. ## 浮点数
  3. - f32
  4. - f64(未标注类型默认使用)
  5. <a name="bVcJG"></a>
  6. ## 布尔值
  7. - true/false,大小是一个字节
  8. <a name="MnRyf"></a>
  9. ## 字符
  10. - 单个字符,使用单引号包装
  11. - `let c = 'z'`
  12. <a name="PNtdq"></a>
  13. # 复合类型
  14. <a name="OIngO"></a>
  15. ## 元组
  16. - 组合多个各种类型的值
  17. - 有固定长度,一旦声明就不能修改长度
  18. ```rust
  19. let mytuple: (i32, char) = (10, 'a');
  20. // 解封装:
  21. // 模式匹配 let (c, d) = mytuple;
  22. // 索引访问 mytuple.0,mytuple.1

数组

  • 一系列类型相同的数据
  • rust 编译时对于数组的访问有越界检查 ```rust // [类型; 长度] let myarray: [u32; 5] = [1, 2, 3, 4, 5];

// 但是这样用index访问编译不会报错,会在运行时报错 let myarray: [i32; 5] = [1, 2, 3, 4, 5]; // unwrap 是 parse 失败时让程序崩溃 let index = “5”.parse::().unwrap(); println!(“item: {}”, myarray[index]);

  1. <a name="i4XCc"></a>
  2. ## 切片
  3. - 是对一个数组(包括固定大小数组和动态数组)的引用片段
  4. - 有利于安全有效地访问数组的一部分,不需要copy数组中的内容
  5. - 切片在编译时长度未知
  6. - 底层实现
  7. - 一个切片保留着两个usize成员
  8. - 一个指向切片起始位置的指针,一个表示长度
  9. ```rust
  10. let arr: [i32; 5] = [1, 2, 3, 4, 5];
  11. let slice = &arr[0..3]; // 取前3个元素
  12. println!("{} {} {} {}", slice[0], slice[1], slice[2], slice.len());

结构体

  • 是多种不同数据类型的组合
    • 与元组类型类似,区别在于我们可以为每个成员命名
  • 可以使用struct关键字创建三种类型的结构:
    • 元组
    • 经典C结构
    • 无字段的单元结构 ```rust // 元组 struct Pair(i32, f32); let pair = Pair(1, 0.1); println!(“{}”, pair.0);

// 经典的 C 结构 struct Person { name: String, age: u8, } let mike = Person { name: String::from(“mike”), age: 20, }; println!(“{}”, mike.name);

// 无字段单元结构,在范型中较为常用 struct Unit;

// 可以使用派生属性让struct可打印(实现std::fmt::Display)

[derive(Debug)]

struct Person { name: String, age: u8, } let mike = Person { name: String::from(“mike”), age: 20, }; println!(“{:?}”, mike); // Person { name: “mike”, age: 20 }

  1. - 结构体和对象的区别:
  2. - 结构体是数据的结合
  3. - 对象是数据+算法的结合
  4. <a name="Uvvo9"></a>
  5. ## 枚举
  6. - 使用enum关键字创建,包含了取值的所有可能情况
  7. - 有多种不同形式的枚举写法:
  8. - 无参数枚举
  9. - 带枚举值的枚举
  10. - * 带参数的枚举
  11. - 枚举通常与 match(模式匹配) 一起使用
  12. ```rust
  13. // 无参数枚举
  14. enum Planet {
  15. Mars,
  16. Earth,
  17. }
  18. // 带枚举值的枚举
  19. enum Color {
  20. Red = 0xff0000,
  21. Blue = 0x0000ff,
  22. }
  23. // 带参数的枚举
  24. enum IpAddr {
  25. IPv4(u8, u8, u8, u8),
  26. IPv6(u8, u8, u8, u8, u8, u8),
  27. }
  28. fn main() {
  29. let localhost: IpAddr = IpAddr::IPv4(127, 0, 0, 1);
  30. // 模式匹配
  31. match localhost {
  32. IpAddr::IPv4(a, b, c, d) => {
  33. println!("{} {} {} {}", a, b, c, d);
  34. }
  35. _ => {} // 任何非IPv4的类型走这条路
  36. }
  37. }

一些语法知识

注释类型

普通注释

  1. //
  2. /*
  3. */

文档注释

  • 是一种markdown格式的注释
    • 用于对文档中的代码生成文档
    • 可以使用 cargo doc 生成 html 文档 ```rust //! 这是模块级别的文档注释, 一般用于模块文件的头部

/// 这是文档注释, 一般用于函数或结构体的说明, 置于说明对象的上方. struct Foo;

  1. <a name="gZ7FF"></a>
  2. ## println的一些用法
  3. - println! 用于将数据打印到标准输出, 且在数据末尾自动带上换行符. 在所有平台上, 换行符都是换行符(没有额外的回车符)
  4. ```rust
  5. // 可以使用额外的位置参数.
  6. println!("{0}{1}{0}", 4, 2);
  7. // 使用命名参数.
  8. println!("name={name} age={age}", name="jack", age=6);
  9. // 可以在 `:` 后面指定特殊的格式.
  10. println!("{} of {:b} people know binary, the other half don't", 1, 2);
  11. // 可以按指定宽度来右对齐文本.
  12. println!("{number:>width$}", number=1, width=6);
  13. // 在数字左边补 0.下面语句输出 "000001".
  14. println!("{number:>0width$}", number=1, width=6);
  15. // println! 会检查使用到的参数数量是否正确.
  16. println!("My name is {0}, {1} {0}", "Bond");
  17. // 编译将会报错, 请补上漏掉的参数:"James"

类型转换

  • Rust 是强类型语言,不支持隐式转换
  • Rust 为类型转换提供了几种不同的方法:as/transmute

    as 语法

  • 最基础的转换方法,通常用于整数、浮点数、字符数据之间的类型转换

    1. fn main() {
    2. let a: i8 = -10;
    3. let b = a as u8;
    4. println!("a = {}, b = {}", a, b); // a = -10, b = 246
    5. }

    数值转换的语义:

  • 两个相同大小的整型之间转换是一个no-op(比如:i32 -> u32)

  • 从一个大的整型到小的,会截断
  • 从小的整型到大的,会:
    • 如果源类型无符号会补0(zero-extend)
    • 有符号会补符号 (sign-extend)
  • 从一个浮点转整型,会向0舍入
  • 从一个整型转浮会产生整型的浮点表示,如有必要会舍入(未指定舍入策略)
  • 从f32 -> f64 是完美无缺的,反之则产生最接近值

    transmute

  • as 只允许安全的转换(例如会拒绝尝试将4个字节转换为u32)

    1. let a = [0u8, 0u8, 0u8, 0u8];
    2. // 这种非安全转化会报错
    3. let b = a as u32;
  • 但是我们知道u32在内存中为4个连续的u8,所以可以告诉编译器以另一种数据类型对待内存中的数据

  • 除非清楚知道自己在干什么,不然不推荐使用transmute
  • 使用transmute,需要将代码写入unsafa块中 ```rust use std::mem;

fn main() { unsafe { // 字节序,Rust 默认小端序 // 00000000 10000000 00000000 00000000 let a = [0u8, 1u8, 0u8, 0u8]; // b = 0b1_00000000 let b = mem::transmute::<[u8; 4], u32>(a); println!(“b = {}”, b); // 256 let c: u32 = mem::transmute(a); println!(“c = {}”, c); // 256 } } ```