头图

1 Overview

Rust is a compiled language (AOT - ahead of time), which needs to be compiled before generating executable code, which is fundamentally different from interpreted languages such as JavaScript.

2. Variables and Mutability

In Rust, variables are declared by let, but variables declared by let are immutable (_Immutable_) variables by default.

If you try to modify the value of a variable after the let declaration, an error will be reported at compile time.

fn main() {
    let a = 5;
    a = 6; // error: cannot assign twice to immutable variable
}

We can use the following two ways to solve this problem

  • When a variable is declared, let is modified with the mut keyword, indicating that it is a mutable variable. It should be noted that Rust is a strongly typed language, so even if declared as a mutable variable, it can only be reassigned to a value of the same data type

    fn main() {
      let mut a = 5;
      a = 6;
      // 改变数据类型,编译报错
      a = "6"; // error: expected integer, found `&str`
    }
  • Using the shadowing feature, declaring this variable again overrides the previous value and is not restricted by the previous data type. Equivalent to re-declaration, the previous variable is hidden

    fn main() {
      let a = '5';   // '5'
      let a = 5;     // 5
      let a = a + 1; // 6
      let a = a * 2; // 12
    }

3. Constants

In Rust, constants are declared through the const keyword. The difference between constants and variables is

  • The mut keyword cannot be used
  • Constants must specify a data type when they are declared
  • Constants can be declared at any scope, including the global scope
  • Constants can only be bound to constant expressions

Naming convention: all letters are capitalized, and words are connected by _, such as

const MAX_AGE: u32 = 10_0000;

4. Data Types

4.1 Scalar Types

4.1.1 Integer Types

  • Signed integers start with i $$[-(2^n-1) , 2^{n-1}-1]$$
  • Unsigned integers start with u $$[0 , 2^n - 1]$$
  • The number of bits of isize and usize types is determined by the architecture of the computer the program runs on. On a 64-bit computer, it is 64-bit.
  • The default type for integers is i32

截屏2022-02-01 下午9.48.42.png

4.1.1.1 Integer literals
  • Start with 0x in hexadecimal
  • Octal starts with 0o
  • Start with 0b in binary
  • The data type of the byte type is limited to u8, starting with b

截屏2022-02-01 下午10.10.27.png

Except for the byte type, all numeric literals allow type suffixes, such as

// 表示 u8 类型的数值 57
let a = 57u8;

4.1.1.2 Integer overflow

If you set a variable of type u8 (0-255) to 256, the following will happen:

  • In development mode, Rust detects an overflow and panics when the program runs
  • In release mode, Rust doesn't detect overflow, and wraps around when it happens:

    • 256 -> 0
    • 257 -> 1
    • ...

4.1.2 Floating point types

  • f32, single precision
  • f64, double (the default type for Rust floats, since f64 is about as fast as f32 on modern CPUs)
  • Rust floating point types use the IEEE-754 standard

4.1.3 Boolean Type

  • occupies one byte
  • symbol is bool, value is true | false

4.1.4 Character Type

  • The symbol is char
  • Use single quotes for literal values

4.2 Composite Types

4.2.1 Tuples

  • Immutable length after declaration
  • Values of different data types can be grouped together

    // 声明一个元组
    let tup: (i32, f64, u8) = (500, 5.6, 23);
    
    // 获取元组成员
    
    // 1.解构
    let (x, y, z) = tup;
    println!("{}, {}, {}", x, y, z); // 500, 5.6, 23
    
    // 2.点标记法
    println!("{}, {}, {}", tup.0, tup.1, tup.2); // 500, 5.6, 23

4.2.2 Arrays

  • Like tuple, the length is immutable after declaration
  • The data types of the array members must be the same
  • Arrays are stored in stack memory
  • The type of the array is represented by the form [type; length]

    let arr: [i32; 5] = [1,2,3,4,5];
    
    // 特殊的数组声明方法
    let sameMemberArray = [3; 5]; // [3,3,3,3,3]
    
    // 访问数组成员
    let f = arr[0]; // 1
    
    // 访问数组越界 - rust编译器会进行简单的越界检查
    let more = arr[6]; // 编译报错
    
    // 访问数组越界 - rust编译器检测不到的场景
    let temp = [6,7,8,9];
    let more_temp = arr[temp[0]]; // 编译通过,运行时报错

5. Functions

  • Declare a function with the fn keyword in Rust
  • For function and variable names, use snake case conventions (words are lowercase, spliced with _)
  • Parameters must specify a data type when they are defined
  • If you want to return a value early, use the return keyword
  • The default return value of the function is an empty tuple

    fn main() {
      let number = get_number(5);
      
      println!("{}", number); // 10
    }
    
    fn get_number(a: i32) -> i32 {
      // 函数中最后行如果是表达式,那么表达式的值则会作为函数的返回值
      // 如果行尾加了分号,则会被识别为语句,就不会被作为函数的返回值
      a + 5
    }

6. Control Flow

6.1 Conditional branch - if

  • It should be noted that if each branch block has a return value, the data type must be the same

    let a = 3;
    
    // if else
    if a == 3 {
      println!("a is 3");
    } else {
      println!("a is not 3");
    }
    
    // 使用 表达式 的特性达到其他语言中 三元表达式 的效果
    let b = if a == 3 { 5 } else { 6 }; // 5 

6.2 Loops

  • loop

    • loop will execute the code in the loop body in an infinite loop until interrupted by break
    • break can provide a return value for the loop expression
    // loop 循环
    let mut count = 0;
    
    let value = loop {
      count += 1;
      
      if count == 10 {
          break count * 2;
      }
    }
    
    println!("{}", value); // 20
  • while

    let arr = [1,2,3,4,5];
    let mut index = 0;
    
    // 使用 while 循环遍历数组
    while index < 5 {
      println!("{}", arr[index]);
      
      index = index + 1;
    }


  • for

    let arr = [1,2,3,4,5];
    
    for item in arr.iter() {
      println!("for item {}", item);
    }
    
    // 使用 range 实现指定次数的循环
    // 1. (1..5) -> 一个包含 1,2,3,4 的可迭代器
    // 2. 使用 rev 方法进行反转
    for item in (1..5).rev() {
      println!("range item is {}", item)
    }

7. Ownership

Ownership is a core feature of Rust that ensures memory safety without GC

7.1 Memory and Allocation

When the variable goes out of scope, Rust will automatically call the drop function to return the memory space to the operating system

7.2 Data copy on Stack: copy

  • For simple scalar data types, the following code will eventually push two 5's onto the stack

    • Essentially because scalar types implement the Copy trait
    • Copy This trait is used for data types such as integers that are stored on the stack. Data types that need to allocate memory cannot implement this trait.
    • If the Copy trait is implemented, the old variable can still be used after assignment
    • If a type implements the Drop trait, Rust will not allow it to implement the Copy trait.
    • Data types with the Copy trait

      • integer
      • floating point number
      • boolean
      • character
      • Ancestor (need to satisfy the data types that all members have Copy trait)
    let x = 5;
    let y = x;
    
    println!("{}, {}", x, y); // 5, 5

7.3 The way variables interact with data: move

  • Corresponding to the composite data type of unknown length, after assigning one variable to another variable, the former will become invalid (called move in Rust, that is, the memory space pointed to by s1 is moved to s2, and after the move is completed, s1 will become invalid. , so as to avoid two release operations for the same memory space when s1 and s2 go out of scope)

    • Double free is a serious bug in other languages that require manual memory control, and may free memory that is being used by other programs, causing unknown problems
    let s1 = String::from("haha");
    let s2 = s1;
    
    println!("{}", s1); // error: value borrowed here after move

7.4 Ownership and Functions

  • In fact, the situation of passing values to functions and variables is similar, move or copy will occur

    fn main() {
      let string = String::from("hello");
    
      // Move,所有权发生移动,传入到了函数作用域中
      move_case(string);
    
      /*
       * 在调用 move_case 时,string指向的内存空间 的所有权发生了 move,
       * 当 move_case 调用完毕时,string指向的内存空间已经被释放了,
       * 所以之后再访问 string,编译时就会报错
       */
      // println!("{}", string); // value borrowed here after move
    
      // ---------------------------------------------------------------------
    
      let number = 12;
    
      // Copy,传入 number 值的副本
      copy_case(number);
    
      /*
       * number 是简单的标量类型,实现了 Copy trait
       * 在调用 copy_case 时,仅仅是传入了一个副本,
       * 所以后续任然可以继续使用
       */
      println!("{}", number);
    
      // ---------------------------------------------------------------------
    
      let bar = String::from("bar");
    
      /*
       * 在以下函数的调用过程中,bar 指向的内存空间的 所有权 被移动到了 函数作用域 中,
       * take_ownership_and_return 这个函数的作用是得到一个内存空间的所有权并将其返回,
       * 最终 foo 拿到了该内存空间的 所有权,其实这段代码的效果与 let foo = bar; 相同
       */
      let foo = take_ownership_and_return(bar);
    
      println!("{}", foo)
    }
    
    fn move_case(string: String) {
      println!("{}", string);
    }
    
    fn copy_case(number: u32) {
      println!("{}", number);
    }
    
    fn take_ownership_and_return(s: String) -> String {
      s
    }

8. Stack & Heap

  • stack is a contiguous memory space
  • The heap is the hashed memory space, and the pointer to the heap memory is stored in the stack
  • All data stored on the stack must have a known size, data whose size is unknown at compile time or whose size may change at runtime must be stored on the heap

木木剑光
2.2k 声望2k 粉丝

Ant Design 社区贡献者