RUST所有权和内存模型
借用和引用
在深入所有权之前,先再次介绍引用。引用是一种Rust
提供的指针语义,它基于指针实现,引用可以看作某块内存的别名,它需要满足编译器的各种安全检查规则。我们使用&
表示不可变应用类型,同时使用&mut
表示可变引用类型。接着介绍借用的概念,通过&
接上一个变量,可以实现对于所有权的借用,借用所有权并不会让所有权发生转移,但是所有者会受到如下的限制:
- 在不可变借用期间,所有者不能修改内存内容,也不能再次出借为可变借用。
- 在可变借用期间,所有者不能访问内存内容,并且不能再次出借。
当借用者离开作用域时,所有权就会归还。同时借用有如下的规则:
-
借用的声明周期不能长于出借方的声明周期。
-
可变借用不能有多个。
-
不可变借用不能出借为可变借用。
这里举几个常见例子。
例子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
声明为一个可变的vec
,b
接着对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
}
如上的代码可以被正确的执行,因为它并不违反可变借用只能有一个的原则,这里b
对a
进行了可变借用,但是b右将所有权交给了c
,从始至终只有一个可变引用,不违反借用规则。
例子4
fn main() {
let mut a = 1;
let b = &a;
let c = &a;
println!("{} {}", b, c);
}
这里由于默认是不可变借用,所以根据借用规则,不可变借用可以有多个。
内存模型
这里介绍了各种只能指针和引用在内存是如何分布的,大部分都是使用一个类似指针的一个变量指向内存的一个位置。