1 关键字

只列出C++中没有的,相同含义的不列出:

关键字 含义
as 强制类型转换, 消除特定包含项的 trait 的歧义, 或者对 use 和 extern crate 语句中的项重命名
crate 链接( link) 一个外部 crate 或一个代表宏定义的 crate 的宏变量
dyn 动态分发 trait 对象
fn 定义一个函数或 函数指针类型 (function pointer type)
for 遍历一个迭代器或实现一个 trait 或者指定一个更高级的生命周期
impl 实现自有或 trait 功能
in for 循环语法的一部分
let 绑定一个变量,默认不可修改,可以多次使用并改变类型
loop 无条件循环
match 模式匹配
mod 定义一个模块
move 使闭包获取其所捕获项的所有权
mut 表示引用、 裸指针或模式可以修改,但不能改变类型
pub 表示结构体字段、 impl 块或模块的公有可见性
ref 通过引用绑定
Self 实现 trait 的类型的类型别名
self 表示方法本身或当前模块
static 表示全局变量或在整个程序执行期间保持其生命周期
super 表示当前模块的父模块
trait 定义一个 trait
type 定义一个类型别名或关联类型
unsafe 表示不安全的代码、 函数、 trait 或实现
use 引入外部空间的符号
where 表示一个约束类型的从句
常量 const MAX_POINTS: u32 = 100_000;

2 数据类型

在 Rust 中, 每一个值都属于某一个 数据类型( data type) , 这告诉 Rust 它被指定为何种数据, 以便明确数据处理方式。 我们将看到两类数据类型子集: 标量( scalar) 和复合( compound)。

Rust 有四种基本的标量类型: 整型、 浮点型、 布尔类型(bool)和字符类型(char)

2.1 整型

长度 有符号 无符号
8-bit i8 u8
16-bit i16 u16
32-bit i32(默认类型) u32
64-bit i64 u64
128-bit i128 u128
arch(依赖系统架构,64或32) isize usize

Rust的浮点数类型是 f32 和 f64 , 分别占 32 位和 64 位。 默认类型是 f64 , 因为在现代 CPU中, 它与 f32 速度几乎一样, 不过精度更高。

整型的运算存在类型范围溢出的问题,rust提供了带前缀的运算函数对运算结果进行处理:

  • Checked 前缀返回一个结果的 Option 值: 如果运算结果可以被结果类型正确表示就返回 Some(v),否则返回 None

    1. // 10 和 20 的结果可以用 u8 表示
    2. println!("{}", (10_u8).checked_add(20).unwrap());
    3. //100 和 200 的和不能用 u8 表示
    4. println!("{}", (100_u8).checked_add(200).unwrap());
    5. println!("{}", (100_u8).checked_add(200).expect("result is too big"));
  • Wrapping 前缀返回正确的值对结果类型能表示的范围的余数

    1. println!("{}", (100_u16).wrapping_mul(200));
    2. println!("{}", (500_u16).wrapping_mul(500));//超出范围,因此我们得到 250000 对 2^16 取模
  • Saturating 前缀会返回被“截断”到这个类型能表示的最大或最小值

    1. println!("{}", 32760_i16.saturating_add(10));//i16最大为32767,add超过范围
  • Overflowing 前缀返回一个 tuple(reulst, overflowed), result为wrapping计算的结果,overflowed表示是否溢出

    1. //对u16来说移动 17 位太多了,2^17 对 2^16 取余等于 10
    2. let (result, flag) = 5_u16.overflowing_shl(17);
    3. println!("result:{}, overflowed:{}", result, flag);

    2.2 char

    Rust 的字符类型char代表一个单独的 Unicode字符(UTF-8),是一个 32 位的值
    Rust 永远不会进行 char 和其他任何类型之间的隐式转换

  • 可以使用as将char转换为i32的整数,小于i32的类型会被截断

  • 整数转换为char,可以使用as(只能用于u8类型)或std::char提供的转换函数 ```rust //使用as转换为32位整数 assert_eq!(‘‘ as i32, 42); //整数转换为char assert_eq!(42_u8 as char, ‘‘);

assert_eq!(‘8’.to_digit(10), Some(8));//字符8转换为10进制2 assert_eq!(std::char::from_digit(2, 10), Some(‘2’));//2转换位字符2

  1. <a name="E7QKs"></a>
  2. ## 2.3 复合类型
  3. <a name="rF7aq"></a>
  4. ### 元组
  5. 元组是一个将多个其他类型的值组合进一个复合类型的主要方式。** 元组长度固定:** 一旦声明, 其长度不会增大或缩小。
  6. ```rust
  7. let tup: (i32, f64, u8) = (500, 6.4, 1); //定义元组
  8. //元组解构
  9. let (x, y, z) = tup; //使用新变量保存解构后值
  10. let first = tup.0; //指定获取元组的哪个值,不能使用i遍历

数组

Rust 中, 数组中的值位于中括号内的逗号分隔的列表中:

let a = [1, 2, 3, 4, 5];
//或
let a: [i32; 5] = [1, 2, 3, 4, 5];//显示指定类型和个数
//或
let a = [1; 5] //指定个数和默认值

vector

一个 Vec 是一个长度可变的类型 T 的数组,它的元素都存储在堆上。创建vector的方法如下:

  • Vec::new()空的vector,并push元素
  • vec!宏来创建vector(相当于自动完成上面的操作)
  • 使用迭代器collect()
    //创建vector方法1
    let mut pal = Vec::new();
    pal.push("step");
    pal.push("on");
    pal.push("no");
    pal.push("pets");
    assert_eq!(pal, vec!["step", "on", "no", "pets"]);
    //创建vector方法2
    let mut primes = vec![2, 3, 5, 7];
    assert_eq!(primes.iter().product::<i32>(), 210);
    //创建vector方法3
    let v: Vec<i32> = (0..5).collect();//需要指定v类型
    assert_eq!(v, [0, 1, 2, 3, 4]);
    

2.4 String类型

String类型不是基本类型,是标准库提供的扩展类型,有几种方式创建 String:

  • .to_string()方法把 &str 转换为一个 String,这会拷贝字符串:

    let error_message = "too many pets".to_string();
    
  • format!()宏类返回一个新的 String, 而且它不会再最后自动加上换行符:

    assert_eq!(format!("{}°{:02}′{:02}′′N", 24, 5, 23), "24°05′23′′N".to_string());
    
  • 字符串的数组、切片、vector 都有两个方法.concat().join(sep),把多个字符串组合成一个:

    let bits = vec!["veni", "vidi", "vici"];
    assert_eq!(bits.concat(), "venividivici");
    assert_eq!(bits.join(", "), "veni, vidi, vici");
    

Rust提供一些类似字符串的类型用于不同的场景:

  • 对于 Unicode 文本坚持使用 String 和 &str。
  • 当处理文件名时,使用 std::path::PathBuf 和 &Path 来代替。
  • 当处理完全不是 UTF­8 编码的二进制数据时,使用 Vec 和 &[u8]。
  • 当处理环境变量名称或者命令行参数这些由操作系统提供的内容时,使用 OsString 和 &OsStr。
  • 当和以空字符结尾的 C 库交互时,使用 std::ffi::CString 和 &CStr。

    2.5 struct结构体

    结构体定义和C++基本一致,不同的是成员末尾使用逗号:

    struct Rectangle {
      width: u32,
      height: u32,
    }
    

    可以使用impl为结构体添加成员函数(C++是可以直接写在结构体中,rust没有类的概念,所以需要写在结构体外):

    impl Rectangle {
      fn area(&self) -> u32 {
          self.width * self.height
      }
    
      fn can_hold(&self, other: &Rectangle) -> bool {
          self.width > other.width && self.height > other.height
      }
    }
    //调用函数:
    fn main() {
      let rect1 = Rectangle {
          width: 30,
          height: 50,
      };
    
      println!("rect1 is {}", rect1.area());
    }
    

    而对于下面这种成员函数,参数中没有self,意味着这属于类不属于对象(C++的概念),所以调用时需要使用结构体名::函数名: ```rust impl Rectangle { fn square(size: u32) -> Rectangle {

      Rectangle {
          width: size,
          height: size,
      }
    

    } }

fn main() { let sq = Rectangle::square(3); }

<a name="6rEOY"></a>
# 3 函数
Rust 代码中的函数和变量名使用 snake case 规范风格。 在 snake case 中, 所有字母都是小写并使用下划线分隔单词。<br />Rust 中的函数定义**以 fn 开始并在函数名后跟一对圆括号**。 大括号告诉编译器哪里是函数体的开始和结尾。Rust 不关心函数定义于何处, 只要定义了就行。
<a name="xjEvb"></a>
## 3.1 函数参数
```rust
fn another_function(x: i32, y: i32) {//参数和类型
    println!("The value of x is: {}", x);
    println!("The value of y is: {}", y);
}

3.2 返回值

函数可以向调用它的代码返回值。 我们并不对返回值命名, 但要在箭头( -> ) 后声明它的类型。使用 return 关键字指定返回值。

fn another_function(x: i32) -> i32 {//返回值类型
    println!("The value of x is: {}", x);
    return 100;
}

4 控制流

4.1 if语句

所有的 if 表达式都以 if 关键字开头, 其后跟一个条件(不需要括号):

fn main() {
    let number = 3;
    //if语句,表达式结果必须为bool类型
    if number % 4 == 0 {
        println!("number is divisible by 4");
    } else if number % 3 == 0 {
        println!("number is divisible by 3");
    } else if number % 2 == 0 {
        println!("number is divisible by 2");
    } else {
        println!("number is not divisible by 4, 3, or 2");
    }
}

注意

  • Rust 并不会尝试自动地将非布尔值转换为布尔值。 必须总是显式地使用布尔值作为 if的条件。
  • enum类型不能直接在if里判断(因为enum类没有实现==运算符),需要借助**match****if let**进行判断

    4.2 循环

    Rust 有三种循环: loop 、 while 和 for

    loop

    loop可以执行一个循环,但是没有终止条件,需要在内部使用break返回,同时break可以携带返回值,将loop的最终结果赋给一个变量
    let mut counter = 0;
      let result = loop {//loop返回值赋给了result变量
          counter += 1;
          if counter == 10 {
              break counter * 2; //注意,break可以携带返回值
          }
      };
    

    while

    fn main() {
      let mut number = 3;
      while number != 0 {
          println!("{}!", number);
          number = number - 1;
      }
    }
    

    for

    fn main() {
      let a = [10, 20, 30, 40, 50];
      for element in a.iter() {                 //直接生成一个index的迭代器遍历,不直接使用index,防止越界
          println!("the value is: {}", element);
      }
      println!();
      for element in a.iter().rev() {          //rev用来反转index序列,从后往前输出
          println!("the value is: {}", element);
      }
      println!();
      for i in 1..3 {                          //直接用index,不用迭代器
          println!("the value is: {}", a[i]);
      }
    }