借用和引用

​ 在深入所有权之前,先再次介绍引用。引用是一种Rust提供的指针语义,它基于指针实现,引用可以看作某块内存的别名,它需要满足编译器的各种安全检查规则。我们使用&表示不可变应用类型,同时使用&mut表示可变引用类型。接着介绍借用的概念,通过&接上一个变量,可以实现对于所有权的借用,借用所有权并不会让所有权发生转移,但是所有者会受到如下的限制:

  • 在不可变借用期间,所有者不能修改内存内容,也不能再次出借为可变借用。
  • 在可变借用期间,所有者不能访问内存内容,并且不能再次出借。

​ 当借用者离开作用域时,所有权就会归还。同时借用有如下的规则:

  1. 借用的声明周期不能长于出借方的声明周期。

  2. 可变借用不能有多个。

  3. 不可变借用不能出借为可变借用。

这里举几个常见例子。

例子1

fn main() {
    let mut a = vec![1, 2, 3];
    let b: &mut Vec<i32> = &mut a;
    
    for i in b {
        println!("{}", i);
    }
    
    println!("{}", b[0]);
}

​ 这里将a声明为一个可变的vecb接着对a进行一次可变引用。但是b,这里在for i in b这个语句中,会自动调用into_iter(b)导致了所有权的转移,接着使用b[0]就会出错。但是如果改为b.iter(),会自动扩展为(&mut *b).iter(),这样循环结束后,由于再借用结束,b就可以继续使用。

例子2

impl List {
    //...
    pub fn push(&mut self, elem: i32) {
        let new_code = Box::new(Node {
            elem:
            next: self.head
        })
        
        self.head = Link::More(new_node);
    }
}

​ 如上是一个栈的实现代码,这里next: self.head会报错,因为self这里已经被借用,被借用后就不能再移动了,除非所有权被归还。

例子3

#[derive(Debug)]
struct Student {
    age: i32,
    id: i32,
}

fn main() {
    let mut a = Student { age: 1, id: 0 };
    let b = &mut a.age;
    let c = b;
    *c = 3
}

如上的代码可以被正确的执行,因为它并不违反可变借用只能有一个的原则,这里ba进行了可变借用,但是b右将所有权交给了c,从始至终只有一个可变引用,不违反借用规则。

例子4

fn main() {
    let mut a = 1;
    let b = &a;
    let c = &a;
    println!("{} {}", b, c);
}

这里由于默认是不可变借用,所以根据借用规则,不可变借用可以有多个。

内存模型

​ 这里介绍了各种只能指针和引用在内存是如何分布的,大部分都是使用一个类似指针的一个变量指向内存的一个位置。