https://doc.rust-lang.org/std/index.html

main.rs

  1. fn main() {
  2. variables_demo();
  3. const_demo();
  4. }

变量和可变性(Variables and Mutability)

  1. fn variables_demo() {
  2. println!("variables_demo");
  3. let x = 5; //不可变变量 x
  4. println!("This x is {x}");
  5. //x = 6; //对不可变变量 x重新赋值
  6. //println!("This is {x}");
  7. }
  1. cargo run

会出现以下提示信息

  1. PS F:\project\LFC.Rust.Demo\variables> cargo run
  2. Blocking waiting for file lock on package cache
  3. Blocking waiting for file lock on Cargo.lock file
  4. Compiling variables v0.1.0 (F:\project\LFC.Rust.Demo\variables)
  5. error[E0384]: cannot assign twice to immutable variable `x`
  6. --> src\main.rs:5:5
  7. |
  8. 3 | let x =5;//不可变变量 x
  9. | -
  10. | |
  11. | first assignment to `x`
  12. | help: consider making this binding mutable: `mut x`
  13. 4 | println!("This x is {x}");
  14. 5 | x=6;//对不可变变量 x重新赋值
  15. | ^^^ cannot assign twice to immutable variable
  16. For more information about this error, try `rustc --explain E0384`.
  17. error: could not compile `variables` due to previous error
  18. PS F:\project\LFC.Rust.Demo\variables>

Rust 编译器保证,如果声明一个值不会变,它就真的不会变,所以你不必自己跟踪它。这意味着你的代码更易于推导。

常量(constants)

  1. fn const_demo() {
  2. println!("const_demo");
  3. VERSION: &'static str = "0.1.0";
  4. println!("VERSION is:{}", VERSION);
  5. }

:::warning

  • 声明常量使用 const 关键字,并且 必须 注明值的类型
  • 不允许对常量使用 mut。常量不光默认不能变,它总是不能变
  • 常量可以在任何作用域中声明,包括全局作用域 :::

    隐藏(Shadowing)

    rust可以定义一个与之前变量同名的新变量。Rustacean 们称之为第一个变量被第二个 隐藏(Shadowing) 了。

    1. fn shadowing_demo() {
    2. //不可变变量 x
    3. let x = 5;
    4. println!("The value of x is: {x}");
    5. //创建了一个新变量 x,获取初始值并加 1,隐藏了第一个x
    6. let x = x + 1;
    7. println!("The value of x + 1 is: {x}");
    8. {
    9. let x = x * 2;
    10. println!("The value of x * 2 is: {x}");
    11. }
    12. println!("The value of x is: {x}");
    13. }

    :::warning 这个程序首先将 x 绑定到值 5 上。接着通过 let x = 创建了一个新变量 x,获取初始值并加 1,这样 x 的值就变成 6 了。然后,在使用花括号创建的内部作用域内,第三个 let 语句也隐藏了 x 并创建了一个新的变量,将之前的值乘以 2,x 得到的值是 12。当该作用域结束时,内部 shadowing 的作用域也结束了,x 又返回到 6。 :::

    1. The value of x is: 5
    2. The value of x + 1 is: 6
    3. The value of x * 2 is: 12
    4. The value of x is: 6

    复合类型(Compound types)

    复合类型Compound types)可以将多个值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。

    元组类型(tuple)

    https://doc.rust-lang.org/std/primitive.tuple.html
    元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定:一旦声明,其长度不会增大或缩小。

    1. fn tup_demo() {
    2. println!();
    3. let tup: (i32, i32, f32) = (11, 100, 1000.1);
    4. println!("The value of x is: {}", tup.0);
    5. //解构
    6. println!();
    7. let tup2 = (500, 6.4, 1);
    8. let (x, y, z) = tup2;
    9. println!("The value of x is: {x}");
    10. println!("The value of y is: {y}");
    11. println!("The value of z is: {z}");
    12. //使用点号(.)后跟值的索引来直接访问
    13. println!();
    14. let tup3: (i32, f64, u8) = (500, 6.4, 1);
    15. println!("The value of tup3.0 is: {}", tup3.0);
    16. println!("The value of tup3.1 is: {}", tup3.1);
    17. println!("The value of tup3.2 is: {}", tup3.2);
    18. }

    不带任何值的元组有个特殊的名称,叫做 单元(unit) 元组。这种值以及对应的类型都写作 (),表示空值或空的返回类型。如果表达式不返回任何其他值,则会隐式返回单元值。

    数组类型

    https://doc.rust-lang.org/std/primitive.array.html
    与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,Rust中的数组长度是固定的。
    创建数组有两种语法形式:

  • 包含每个元素的列表,即 .[x, y, z]

  • 重复表达式 ,它生成一个包含 副本的数组。的类型必须为“复制”。[x; N]Nxx

    1. fn array_demo() {
    2. println!();
    3. //我们将数组的值写成在方括号内,用逗号分隔:
    4. let arr = [1, 2, 3, 4, 24, 2, 14, 22];
    5. println!("The value of arr first is: {}", arr[0]);
    6. //方括号中指定初始值加分号再加元素个数[初始值; 元素个数]
    7. println!();
    8. let a = [3; 5];
    9. for x in a {
    10. print!("{x} ");
    11. }
    12. println!();
    13. // 通过引用进行迭代
    14. println!();
    15. let array: [i32; 8] = arr;
    16. for item in array.iter().enumerate() {
    17. let (i, x): (usize, &i32) = item;
    18. println!("array[{i}] = {x}");
    19. }
    20. }

    函数(Functions)

    Rust 代码中的函数和变量名使用 snake case 规范风格。在 snake case 中,所有字母都是小写并使用下划线分隔单词。

    什么是所有权?

    Rust 的核心功能(之一)是 所有权ownership)。虽然该功能很容易解释,但它对语言的其他部分有着深刻的影响。 :::warning

  1. Rust 中的每一个值都有一个被称为其 所有者owner)的变量。
  2. 值在任一时刻有且只有一个所有者。
  3. 当所有者(变量)离开作用域,这个值将被丢弃。 :::

    引用与借用

    引用的规则 :::warning
  • 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
  • 引用必须总是有效的。 :::

    引用(reference)

    引用reference)像一个指针,因为它是一个地址,我们可以由此访问储存于该地址的属于其他变量的数据。与指针不同,引用确保指向某个特定类型的有效值。下面是如何定义并使用一个(新的)calculate_length 函数,它以一个对象的引用作为参数而不是获取值的所有权: ```rust fn reference_demo() { let str = String::from(“hello”); let len = calculate_length(&str);

    println!(“The length of ‘{}’ is {}.”, s1, len); } fn calculate_length(s: &String) -> usize { s.len() }

  1. 我们传递 &s1 calculate_length,同时在函数定义中,我们获取 &String 而不是 String。这些 & 符号就是 **引用**,它们允许你使用值但不获取其所有权。图 4-5 展示了一张示意图。<br />![](https://cdn.nlark.com/yuque/0/2022/svg/12561771/1661489383868-b5748693-728a-4e6a-a589-19121f01667d.svg#clientId=u9b78c57d-9c8b-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=317&id=ueb3e5d6c&margin=%5Bobject%20Object%5D&originHeight=130&originWidth=300&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=ua9038022-c01b-437b-8c49-9221c207b2e&title=&width=732)
  2. :::warning
  3. 注意:与使用 & 引用相反的操作是 解引用(dereferencing),它使用解引用运算符,*。我们将会在第八章遇到一些解引用运算符,并在第十五章详细讨论解引用。
  4. :::
  5. <a name="vALNZ"></a>
  6. ### 借用
  7. 我们将创建一个引用的行为称为 **借用**(_borrowing_)。正如现实生活中,如果一个人拥有某样东西,你可以从他那里借来。当你使用完毕,必须还回去。我们并不拥有它。
  8. ```rust
  9. //可变引用
  10. fn reference_mut_demo() {
  11. println!();
  12. let mut str = String::from("hello");
  13. change(&mut str);
  14. println!("The str is '{}' ", str);
  15. }
  16. fn change(some_string: &mut String) {
  17. some_string.push_str(", world");
  18. }

悬垂指针

在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针dangling pointer),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。

Slice 类型

slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合。slice 是一类引用,所以它没有所有权。

字符串 slice

字符串 slicestring slice)是 String 中一部分值的引用,它看起来像这样:

  1. //字符串 slice
  2. fn string_slice_demo() {
  3. println!();
  4. let s = String::from("hello world");
  5. let hello = &s[0..5];
  6. let world = &s[6..11];
  7. }

hello 是一个部分 String 的引用,由一个额外的 [0..5] 部分指定。可以使用一个由中括号中的 [starting_index..ending_index] 指定的 range 创建一个 slice,其中 starting_index 是 slice 的第一个位置,ending_index 则是 slice 最后一个位置的后一个值。
Rust 基本概念学习 - 图1

结构体(struct)

struct,或者 structure,是一个自定义数据类型,允许你包装和命名多个相关的值,从而形成一个有意义的组合。

结构体定义

定义结构体,需要使用 struct 关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。接着,在大括号中,定义每一部分数据的名字和类型,我们称为 字段field)。

  1. struct User {
  2. active: bool,
  3. username: String,
  4. email: String,
  5. sign_in_count: u64,
  6. }

结构体实例

  1. fn create_user() {
  2. let user1 = User {
  3. email: String::from("someone@example.com"),
  4. username: String::from("someusername123"),
  5. active: true,
  6. sign_in_count: 1,
  7. };
  8. }

类单元结构体

我们也可以定义一个没有任何字段的结构体!它们被称为 类单元结构体unit-like structs)因为它们类似于 (),即“元组类型”一节中提到的 unit 类型。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。

  1. struct AlwaysEqual;
  2. fn main() {
  3. let subject = AlwaysEqual;
  4. }

方法(method)

方法(method)与函数类似:它们使用 fn 关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码。不过方法与函数是不同的,因为它们在结构体的上下文中被定义(或者是枚举或 trait 对象的上下文。并且它们第一个参数总是 self,它代表调用该方法的结构体实例。

定义方法

  1. pub struct User {
  2. active: bool,
  3. username: String,
  4. email: String,
  5. sign_in_count: u64,
  6. }
  7. impl User {
  8. //定义方法
  9. fn set_username(&self, username: String) -> Self {
  10. self.username = username;
  11. }
  12. }

关联函数

所有在 impl 块中定义的函数被称为 关联函数associated functions),因为它们与 impl 后面命名的类型相关。我们可以定义不以 self 为第一参数的关联函数(因此不是方法),因为它们并不作用于一个结构体的实例。

  1. pub struct User {
  2. active: bool,
  3. username: String,
  4. email: String,
  5. sign_in_count: u64,
  6. }
  7. //关联函数
  8. impl User {
  9. //定义方法
  10. fn set_username(&self, username: String) -> Self {
  11. self.username = username;
  12. }
  13. }

多个impl块

每个结构体都允许拥有多个 impl 块。例如,示例 5-16 中的代码等同于示例 5-15,但每个方法有其自己的 impl 块。

  1. pub struct User {
  2. active: bool,
  3. username: String,
  4. email: String,
  5. sign_in_count: u64,
  6. }
  7. //关联函数
  8. impl User {
  9. //定义方法
  10. fn set_username(&self, username: String) -> Self {
  11. self.username = username;
  12. }
  13. }
  14. //关联函数
  15. impl User {
  16. fn get_username(&self) -> String {
  17. self.username;
  18. }
  19. }

枚举(enumerations

枚举是一个不同于结构体的定义自定义数据类型的方式。

枚举值

  1. fn main() {
  2. enum IpAddrKind {
  3. V4,
  4. V6,
  5. }
  6. struct IpAddr {
  7. kind: IpAddrKind,
  8. address: String,
  9. }
  10. let home = IpAddr {
  11. kind: IpAddrKind::V4,
  12. address: String::from("127.0.0.1"),
  13. };
  14. let loopback = IpAddr {
  15. kind: IpAddrKind::V6,
  16. address: String::from("::1"),
  17. };
  18. }

Option枚举

Option 是标准库定义的另一个枚举。Option 类型应用广泛因为它编码了一个非常普遍的场景,即一个值要么有值要么没值。

  1. enum Option<T> {
  2. None,
  3. Some(T),
  4. }
  5. fn main() {
  6. let some_number = Some(5);
  7. let some_string = Some("a string");
  8. let absent_number: Option<i32> = None;
  9. }

控制流结构(match)

Rust 有一个叫做 match 的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行相应代码。
可以把 match 表达式想象成某种硬币分类器:硬币滑入有着不同大小孔洞的轨道,每一个硬币都会掉入符合它大小的孔洞。同样地,值也会通过 match 的每一个模式,并且在遇到第一个 “符合” 的模式时,值会进入相关联的代码块并在执行中被使用。

  1. enum Coin {
  2. Penny,
  3. Nickel,
  4. Dime,
  5. Quarter,
  6. }
  7. fn value_in_cents(coin: Coin) -> u8 {
  8. match coin {
  9. Coin::Penny => 1,
  10. Coin::Nickel => 5,
  11. Coin::Dime => 10,
  12. Coin::Quarter => 25,
  13. }
  14. }
  15. fn main() {}

模块(models)

模块是零个或多个项目的容器。
1661506517934.png
新创建一个models文件夹,添加相应的结构体文件。

  1. pub struct User {
  2. pub active: bool,
  3. pub username: String,
  4. pub email: String,
  5. pub sign_in_count: u64,
  6. }
  7. impl User {
  8. //定义方法
  9. pub fn set_user(username: String) -> User {
  10. User {
  11. active: true,
  12. username: username,
  13. email: String::from("longfuchu@163.com"),
  14. sign_in_count: 22,
  15. }
  16. }
  17. }

Rust 会将一个 xxx.rs 文件默认为一个模块,但是普通的文件夹不能被 Rust 编译器识别。当文件夹下有 mod.rs 文件时,该文件夹才能被识别为模块。
此时模块 user与模块 models还没有直接关系,需要在 mod.rs 文件中引入 user。

  1. pub mod user;

在main.rs将 models模块引入到 crate root 中

  1. mod models;
  1. let user1 = models::user::User {
  2. email: String::from("someone@example.com"),
  3. username: String::from("someusername123"),
  4. active: true,
  5. sign_in_count: 1,
  6. };
  1. mod models;
  2. //结构体
  3. fn create_user() {
  4. println!();
  5. let user1 = models::user::User {
  6. email: String::from("someone@example.com"),
  7. username: String::from("someusername123"),
  8. active: true,
  9. sign_in_count: 1,
  10. };
  11. println!("The user1 is '{}' ", user1.email);
  12. }
  13. fn main() {
  14. create_user();
  15. }