头图

1. Citation

In many scenarios, we may just want to read the value pointed to by a variable and do not want to get its ownership. In this case, we can use reference. In fact, in many other programming languages, there is also the concept of reference.

  • In simple terms, a reference is to create a variable that points to the address of another pointer, instead of directly pointing to the heap memory address pointed to by
  • Get a reference to a pointer variable by using the & address character

For example in Rust we create references like this:

let s1 = String::from("hello");

// 获取 s1 的引用
let s = &s1;
  • The following figure is a good representation of the relationship between and

    • The variable s1 is a pointer address in the stack memory, and the address of String("hello") stored in the heap memory is recorded through ptr
    • The variable s also exists in the stack memory, and the pointer address of s1 is recorded through ptr to realize the reference to String("hello")

截屏2022-02-04 上午11.29.19.png

2. Borrow

The behavior of using to reference as a function parameter is called borrowing, How to use borrowing to avoid a change of ownership of a variable, we can see the following example:

fn main() {
    let s = String::from("hello!");

    // 使用 s 的引用作为入参,s 的所有权就不会发生移动
    let len = get_length(&s);

    println!("the length of {} is {}", s, len); // the length of hello! is 6
}

fn get_length(string: &String) -> usize {
    // 引用和变量一样,默认也是不可变的
    // string.push_str("world"); // `string` is a `&` reference, so the data it refers to cannot be borrowed as mutable

    string.len()
}

3. Mutable references

From the above example, we can know that references, like variables, are immutable by default, and we cannot modify the value pointed to by the reference. But if you want to do that, you can use the mut keyword to make the reference mutable:

fn main() {
    let mut s = String::from("hello!");

    /*
     * 使用 mut 关键字,将 s 的可变引用作为入参,
     * 这样 s 的所有权既不会发生移动,函数中也能通过 可变引用 来修改 s 的值
     */
    let len = get_length(&mut s);
    
    // 在 get_length 函数中我们实现了对 s 的修改
    println!("the length of {} is {}", s, len); // the length of hello!world is 11
}

fn get_length(string: &mut String) -> usize {
    // 通过可变引用对值进行修改
    string.push_str("world");
    string.len()
}

3.1 Important limitations of mutable references

  • Corresponding to a pointer variable, in a specific scope, there can only be one mutable reference. The reason is also easy to understand. If there are two variable references to a variable within a scope, it means that there may be two variables controlling the same memory space at the same time, and data competition will occur It is easy There are bugs at runtime, so Rust avoids such problems by checking at compile time

3.1.1 There can only be one mutable reference in the same scope

As you can see from the example, when we make two variable references to the variable origin, an error will be reported directly at compile time

fn main() {
    let mut origin = String::from("hello");

    let ref1 = &mut origin;
    let ref2 = &mut origin; // error: cannot borrow `origin` as mutable more than once at a time

    println!("{}, {}", ref1, ref2);
}

3.1.2 There can be multiple immutable references

If multiple immutable references are used at the same time, there is no such restriction, because immutable references are actually read-only, and there is no possible memory safety risk. From this example we can see that Rust allows this to be used:

fn main() {
    let origin = String::from("hello");

    let ref1 = &origin;
    let ref2 = &origin;

    println!("{}, {}", ref1, ref2);
}

3.1.3 In some scenarios, there can be multiple mutable references

From the two examples above, we already know that if there are more than two mutable references in a scope at the same time, data races may occur. Are there some scenarios where there will be multiple mutable references? Let's look at the following example:

  • In fact, in the process of program execution, as long as the scope is out of scope, the variables in the scope will be released. In this example, when ref2 is declared, ref1 has been destroyed, so the first principle of variable reference can still be guaranteed.

    fn main() {
      let mut origin = String::from("hello");
    
      {
          let ref1 = &mut origin;
          
          println!("{}", ref1);
      }
    
      // 当 ref2 声明时,ref1 已经被销毁了
      let ref2 = &mut origin;
    
      println!("{}", ref2);
    }

3.1.4 You cannot have a mutable reference and an immutable reference at the same time

For a pointer variable, its mutable reference and immutable reference are mutually exclusive and cannot exist at the same time. The reason is very simple, a mutable reference can modify the value pointing to the memory space, and when the value is modified, the meaning of an immutable reference does not exist. Therefore, Rust will check at compile time, and if this is the case, it will report an error directly:

fn main() {
    let mut origin = String::from("hello");

    let ref1 = &origin;
    let ref2 = &mut origin;// cannot borrow `origin` as mutable because it is also borrowed as immutable

    println!("{} {}", ref1, ref2);
}

4. Dangling references

This kind of bug scenario that appears in languages such as C/C++ describes a scenario where the memory space pointed to by a variable has been released or allocated for use by other programs, but the variable is still valid. In Rust, the compiler checks for this scenario to avoid dangling references. Assuming the compilation passes, the following is a scenario that will generate a dangling reference:

fn main() {
    let s = String::from("haha");

    // s 的所有权被移动到 helper 的作用域中
    let a = helper(s);

    /*
     * 假设编译通过:
     * 当 helper 调用完毕,s 指向的内存空间就被释放了,但在 helper 中返回了 s 的引用,
     * 其实这个引用已经失效,这时 变量a 就变为了一个 悬垂引用
     */
}

fn helper(s: String) -> &String {
    println!("{}", s);
    &s // error: missing lifetime specifier
}

But in fact, at compile time, Rust will not allow the helper to return a reference to s


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

Ant Design 社区贡献者