在对比这几个概念前,我们都知道内存中有 堆(heap)和栈(stack),在 Rust 中我们声明的基本数据类型 字符型
整形
浮点型
布尔型
都是在 栈 中,而其他 复合类型
都在 堆 中,因此使用复杂类型时都是通过指针(Pointer) 进行地址查找并操作
Move,翻译成转移更为准确,表示当前变量所有权的变更;Move不仅发生在变量赋值过程中,在函数传参、函数返回数据时也存在所有权转移
所有权规则:
Rust 中的每一个值都有一个被称为其 所有者(owner)的变量。
值有且只有一个所有者。
当所有者(变量)离开作用域,这个值将被丢弃。
-
所谓转移(Move)(针对复合类型的概念,基本类型实现了Copy,存储在栈中,赋值等操作时直接复制值),首先这个是 Rust 在设计底层语言时,为了保证数据的高效和安全性考虑,给出的一个作用域策略,防止数据竞争
- 堆数据变量 的所有权总是遵循相同的模式:当一个变量赋值给另一个变量时,转移(Move)所有权
- 当持有堆(Heap)中数据值变量,离开作用域时,其值将通过 drop 被清理掉,除非数据被转移(Move)为另一个变量所有
这样就能确保,当前作用域只有一个变量用拥有该值的所有权,那么移动(Move) 就是数据所有权的一次变更
-
引用就是地址的传递,与其他语言概念一致,但只是引用,只读
-
借用(Borrowing)是我们创建引用的这个动作,因此借用是通过引用实现的
We call the action of creating a reference borrowing.
在说的直接点就是使用了&
符号, 借用是通过引用实现的
借用分为:不可变借用(引用:&str)和可变借用(引用:&mut str),在发生可变借用(mut)的时候,会发生所有权的借用转移(Borrowing),
既然这个所有权是借的,那么就会在作用域结束的时候“归还”所有权,或者出现其他变量来获取所有权的时候,丢失所有权。let mut str1 = String::from("hello"); let str2 = &str1; // &str1是一个不可变借用,它赋值给了 str2,因此 str2 指向了 str1 let str3 = &mut str1; // str3 是对 str1 的可变借用,因此 str3 临时获取了原始数据 str1 的所有权 // 但,如果我们在可变引用(借用)之后(即 str3后),使用了引用(str2),那么 Rust 是不允许的, // 因为,此时的所有权归属于 str3, 有修改值的权利,那么 str2 的值也就存在被修改的风险,造成错误, // 那么这个时候,就只有一个变量保有最原始数据("hello")的所有权,就是 ———— str3这也就是(str1 都没有),Rust 所有权及内存回收的机制决定的 // 除非在 str3 之后,str1 又被使用到,重新获取所有权(str3 归还所有权给 str1),如,`str1.push_str("get_ownership_again");` , // println!("str2 = {}", str2); // 错误
总结:
那么,从以上过程可以了解到,同一时间只有一个变量会拥有所有权,要么所有权被转移,要么被借用了。
在同一个作用域内,要么只能有一个可变引用,要么只能有多个不可变引用,如果同时存在,那么可变引用之后,不能继续使不可变引用,
而 要弄清变量是否转移或借用,就从所有权的是否改变进行判断强烈建议阅读这个文章【理解Rust的所有权和borrow规则】
切片是对字符串 String 的 部分引用,如
let s1 = String::from("hello world");
let s2 = &s[0..5];
切片要注意的是,字符串由于使用的UTF-8存储,索引要使用字符边界的索引值,因为类似中文使用了3个字节来存储一个中文,如果边界不正确就会报错,如
let str1 = String::from("你好");
let str2 = &str1[0..1]; // 错误!切片的最小步长是3,但是这个地方长度为1