提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
Rust的所有权概念是其最独特的特性之一,它对Rust的其它部分也有很大影响。所有权概念让Rust在没有GC的情况下,也能够保证内存安全(无需手动管理内存C和C++)。
GC就是garbage collection,比较直观,
Java、Go等均需要。
内存安全广义讲应该包括没有野指针、内存重复释放等错误。进一步的,在rust中结合其它特性可以实现多线程的并发安全访问,C和C++常见的内存泄露则不属于不安全(虽然也是问题)。
一、Ownership是什么?
概括的讲,所有权是rust管理内存的一个规则集。
众所周知,计算机程序都需要管理内存,只是不同的编程语言采用了不同的方法。主要分两类:
- 不断检测未使用内存并释放的GC(Java/go…)
- 程序员手工管理内存(c/c++…)
而Rust采用第三种方法:通过一个包含规则集的所有权系统来管理内存。编译器会检查程序的内存使用是否符合规则集中的规则,违反规则的程序将无法编译通过。另外,这些规则的校验不会带来执行期的开销(编译期检查,编译很慢)。
规则有没有存在漏洞的可能性?导致内存不安全呢?目前看还没有
虽然对很多人来讲,所有权是一个新概念,需要时间来适应。但是,理解所有权是理解Rust独特性的基础。
二、规则集
所有权的规则集主要包括三条,可以类比C++中的RAII、unique_ptr等概念。
- 每个值都有一个称之为owner的变量(有点类似
C++new完必须记得delete) - 任一时刻,每个value只有一个owner(即不可共享,实际多线程是可以共享的,不过需要用到
unsafe等特性) - 程序执行出了owner作用域,对应的值会被丢弃(类似
C++自动调用析构函数)
三、理论+示例
a. 简单情况
{
let s = String::from("hello"); // s is valid from this point forward
// do stuff with s
} // this scope is now over, and s is no longer valid
以上为例,当s的作用域结束之时,即可以将s拥有的值所占用的堆(heap)空间释放。这个机制与C++的RAII( Resource Acquisition Is Initialization)类似,Rust通过调用drop,而C++调用析构函数。
b. 复杂情况
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
对于涉及到堆内存的对象,Rust默认采用浅复制。此外,s2 = s1执行之后,在rust中,s1将不再指向任何String对象,s2单独拥有字符串hello。图中的情况,其实更符合C++中的shared_ptr赋值后(引用计数增加)。

解决了问题,和C++中的
unique_ptr类似,shared_ptr的需求通过Rc、Arc等特性解决。
深复制需要显式声明:
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
}
上述s1.clone()中的clone方法即是深拷贝,当然前提和C++一样,你得正确实现相应的函数,rust 中就是Clone trait。

Copy trait? 有什么意义吗?因为所有类型的对象都可以copy吧?
函数调用的实参,函数返回值,均会转移对象的所有权。
四、引用和借用
引用并没有对应值的所有权,因此不会触发调用drop。引用可以成为借用。
多个变量指向同一个对象,没有共享问题?单线程下不会,多线程下则不允许。
同一个对象不能同时被借用多次,下面代码编译错误。
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
}
这个限制可以在编译期消除数据竞争。
但是,对象已经可以被两个变量修改了啊?可变引用变量+原来的变量,这两者的竞争如何解决呢,多线程不允许共享,如果需要共享,则需要用其它机制。
不可变引用可以有多个,但是可变引用只能有一个。当不可变引用完全无用之前,不能有新的可变引用。(所谓的共享不可变,可变不共享)
编译期检测虚悬引用(dangling)
引用规则:
- 任一时刻,只能由一个可变引用、或者若干不可变引用
- 引用必须一直有效
五、切片类型(slice)
避免下面这种问题,s.clear()之后,s[word]访问其实已经非法,运行期报错。
thread ‘main’ panicked at ‘byte index 5 is out of bounds of ``’, src/main.rs:20:18
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s); // word will get the value 5
s.clear(); // this empties the String, making it equal to ""
// word still has the value 5 here, but there's no more string that
// we could meaningfully use the value 5 with. word is now totally invalid!
}
总结
初看下来,Rust实现内存安全的机制,并没有什么高深的。其实就是可变不共享,共享不可变?
本文深入探讨Rust编程语言的独特特性——所有权系统,包括所有权规则、引用和借用以及切片类型。所有权确保了在没有垃圾回收机制的情况下实现内存安全。所有权规则主要有三条:每个值有一个owner、每次只有一个owner、离开作用域自动释放。引用分为可变和不可变,遵循特定的借用规则,保证了数据安全。切片类型提供了一种共享数据的方式,但需注意其潜在的风险。理解这些概念是掌握Rust内存管理的关键。
485

被折叠的 条评论
为什么被折叠?



