# Rust 入门 ## 目标 1. 能够搭建和配置Rust 开发环境 2. 掌握Rust 基本语法 3. 能写简单的Rust命令行程序 4. 能够完成编译,链接过程 5. 能够执行单元测试 ## Rust 开发环境 参考:<https://hackmd.io/@zenas/rust-app-settings> 需要注意的是,我们需要支持iOS 32位架构,所以需要安装 `1.41.0` 版本,更新的版本已经不支持32位架构了。 ## Rust 基本语法 ### 变量申明 #### 不变 & 可变 作为一门现代编程语言,`Rust` 支持变量不变性,同时也提供了可变量的便利。 1. 使用 `let` 关键字声明一个不变量 `let x = 42;` 2. 使用 `let mut` 声明一个可变量 ```rust= let mut x = 42; x = 77; // 可变,可重新赋值 ``` #### Shadowing 为了减轻变量命名的烦恼,`Rust` 提供了 `shadowing` 能力,可以重复使用同一个变量名: ```rust= let x = 1; // 初始值 let x = to_string(x); // x is shadowing ``` #### 类型推导 `Rust` 提供了强大的类型推导能力,可以自动推导出变量的类型: ```rust= let x = 1; // x is i32 let x: i32 = 1; // with type ``` 对于集合类型,甚至可以在填入第一个元素时,推导出类型: ```rust= let mut x = vec![]; // dont'know the type x.push(1); // the type is Vec<i32> ``` 类型推导是一把双刃剑,好的一面是带来更简洁的代码,同时保证了类型安全;坏的一面是,人类在阅读代码时必须对推导规则烂熟于心,否则难以理解其类型。 但是 Intellij IDE会帮我们提示出推导的类型,可以规避上述负面效果。 !["intellij ide"](https://i.imgur.com/4kv5dRa.png) ### 数据类型 #### 基本数据类型 1. 整数 | Length | Signed | Unsigned | | ------- | ------- | -------- | | 8-bit | `i8` | `u8` | | 16-bit | `i16` | `u16` | | 32-bit | `i32` | `u32` | | 64-bit | `i64` | `u64` | | 128-bit | `i128` | `u128` | | arch | `isize` | `usize` | > copy from <https://doc.rust-lang.org/book/ch03-02-data-types.html> 字面量表示: - 十进制 Decimal: 12_345 - 十六进制 Hex: 0xFF - 八进制 Octal: 0o77 - 二进制 Binary: 0b1111_0001 - 字节 Byte(u8): b'A' 2. 浮点数 - f32 32位单精度 - f64 64位双精度 3. 数字运算:`+` `-` `*` `/` `%` 4. 布尔值(bool): `true` `false` 5. 字符类型(char): 'x', 'y' 6. unit () #### 复合数据类型 1. 元祖 Tuple ```rust= let point = (1, 1); let x = point.0; let y = point.1; ``` 2. 数组 Array ```rust= let arr = [1,2,3]; let arr[i32;3] = [0;3]; let arr_0 = arr[0]; ``` #### 结构体Struct 结构体用于定义关联数据,类似于Java里的`Class`。 1. 定义结构 Rust 支持三种结构体 - 没有字段名称的Tuple元组结构体 - 带有字段名称的结构体 - 不带任何字段的结构体(unit) ```rust= struct Tpl(i32, i32, i32); struct Name { first_name: String, last_name: String, middle_name: String, } struct Phantom; ``` 2. 添加方法 与 Java Class 不同,结构体内只能定义数据,不能定义方法,如果需要定义方法, 则需要使用 `impl` 关键字: ```rust= struct MemberId(String); struct Payer { member_id: MemberId, } struct Payee { member_id: MemberId, } impl Payer { // 添加实例方法 fn pay(&self, amount: Amount, payee: Payee) { todo!(); } // 添加静态方法 fn new(member_id: String) -> Self { return Payer { member_id: MemberId(member_id) } } } ``` 3. 创建实例 - 创建元组类型的struct - 创建带名称的struct 4. 调用方法 对于静态方法,使用 `::` 操作符: ```rust= let payer = Payer::new(String::from("12345")); ``` 对于实例方法,使用 `.` 操作符: ```rust= let amount = Amount(100); let payee = Payee { member_id: MemberId(String::from("56789")) }; // 调用pay方法 payer.pay(amount, payee); ``` #### 枚举Enum Rust 提供了`enum`来支持`sum` 类型的抽象 - 简单枚举 ```rust= enum FeatureSwitch { On, Off, } ``` - 带变量的枚举 支持类似于Struct的匿名元组,或者带名称。 ```rust= enum BotCommand { Standby, MoveForward(u32), Bark { times: u32, pitch: Pitch } } ``` ### 流程控制 #### if 流程控制 `if` 在 `Rust` 里,既可以作为Statement(语句)使用,也可以作为Expression(表达式)使用。和其他语言一样,`if` 基本上有一下三种情形: ```rust= // 只有一个 if if x > 0 { } // if - else if x > 0 { } else { } // if -else if -else if x > 10 { } else if x > 5 { } else { } ``` > if 后面的条件表达式,不需要使用括号 作为表达式使用的例子如下: ```rust= let level = if score > 90 { "A" } else if score > 80 { "B" } else if score > 60 { "C" } else if score > 50 { "D" } else { "E" } ``` ## Hello,Rust World ### 创建命令行程序项目 我们一般使用`cargo`工具来创建项目: ```bash= cargo new --bin hello_rust ``` 完成后, `cargo` 为我们创建了项目的描述文件`Cargo.toml`, 以及可执行文件的入口程序 `src/main.rs`: !["project skeleton"](https://i.imgur.com/jp65iSA.png) 可以看出,Rust 程序的入口其他语言一样,都是`main`函数。 我们需要定义一个`lang`不变量,值为`Rust`,并在控制台输出 `Hello, Rust world!`. ```rust= fn main() { let lang = String::from("Rust"); println!("Hello,{} world!", lang); } ``` ### 构建 ```bash= cargo build ``` 完成构建后,可以在`target`目录下找到相应的可执行程序。 ### 单元测试 只需要在测试用例函数上添加属性标注,就可以使用`cargo`进行单元测试: ```rust= #[test] fn test_hello_count() { let hello = String::from("hello"); println!("test length of {}", hello); assert_eq!(5, hello.len()); } ``` 执行 `cargo test` 即可进行单元测试。 需要注意的是,`cargo test` 默认不会输出打印到控制台的信息,可以增加`--nocapture`或者`--show-output`参数强制输出。 > 所有 cargo 子命令都可以使用 `cargo help <sub-command>`获得帮助信息.