内存管理
内存管理对于程序开发非常重要,编程语言一般有两种方式:
- 垃圾处理机制(garbage collocation):一般高级语言都有gc机制,即语言帮我们做好了一切关于内存的管理,我们不需要管它,所以在程序开发的时候根本就无需管理内存。但问题是在运行时,计算机还需要去寻找不用的内存,然后进行处理,带来的问题就是运行效率降低。
- 手动管理:内存的申请和释放,一切都要手动去管理,好处在于运行时效率很高,问题是特别麻烦,对程序员要去高,内存没有清理干净会带来内存浪费,提前释放会带来后面变量指向失效,多次释放的问题等等。这也是C、C++让人头疼之处。
rust是一门系统级的语言,所以肯定不能使用gc,因为rust要有比肩c/c++的效率,rust的内存管理是一种新的机制:ownership
- 通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。在运行时,所有权系统的任何功能都不会减慢程序。
所有权规则
何为所有权(ownership)?Rust在编译的时候会更加一系列规则来对内存进行检查,所以,所有权就是“一套规则”,即内存管理的规则,所有权规则有三条:
- Rust 中的每一个值都有一个被称为其 所有者(owner)的变量。
- 值在任一时刻有且只有一个所有者。
- 当所有者(变量)离开作用域,这个值将被丢弃。
移动
和显示世界一样,一本书只有一个所有者,“书”是我的了,那就不可能是你的了,只有我是这本书的所有者,即只有我拥有这本书的所有权。
fn main() {
let s1 = String::from("this is a book");
let s2 = s1;
println!("value of s1-{}", s1); // 报错
println!("value of s2-{}", s2); // 没问题
}
一开始s1是这个string的所有者,然后s2=s1操作即将这个string赋值给s1,在其他语言中,s1和s2的值都是这个string(指向string)。但rust中,却不是这样的,s2=s1操作相当与将这个string的所有权交给了s2,此时s1将不再拥有这个string,其带来的结果就是rust将删除s1。这个s2=s1操作叫做”移动“(move)
克隆
如果想要s2和s1都能指向这个string,那么需要进行克隆,这里的不是说s1和s2都指向“同一个”(堆)内存,而是各自指向一个(堆)内存。
fn main() {
let s1 = String::from("this is a book");
let s2 = s1.clone();
println!("value of s1-{}", s1); // 没问题
println!("value of s2-{}", s2); // 没问题
}
s2=s1.clone();操作就是克隆操作。
函数与所有权
再rust中,变量离开作用域就会被丢弃。所以一个变量被其他函数调用之后,即该变量从作用域1跑到了作用域2,函数执行完成之后,在原来的作用域1中,改变了就被丢弃了。这在很多编程语言中是无法想象的,但这就是rust的所有权机制所规定的。
fn greeting(name: String) {
println!("Hello, {}. nice to meet you!", name);
}
fn main() {
let j = String::from("Jack");
greeting(j); // 此时greeting函数是j的所有者
// 在greeting执行完毕,j离开了它所有者(greeting)的作用域,j被清除,main中再无j
println!("{}", j); // 报错
}
小结
变量移动了就不能再用了、被函数调用也不用再用了、离开作用域也不能再用了...看起来好像有很多限制,有点不同于其他语言,这正是rust的所有权机制,它保证了我们不用手动管理内存,从而实现高效的运行效率。
至于所有权机制带来的这些限制,并不是无法解决的,rust为我们提供了相应的概念来解决这些问题。