一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情。
赋值语句
赋值语句几乎在所有编程语言里都是这样:
a = b
就是一个=号。
在大部分编程语言里,赋值语句有下面几点共同点:
-
对于基本变量来说,比如说数字类型,字符类型,布尔类型,这种就是直接拷贝一份,比如说,上面就是把b的值拷贝一份然后塞给a。
-
对于引用类型的变量来说,就是让a也指向b所引用的地方。但是引用的数据,是没有拷贝的。比如说Java里的class,js里的object,等等。
再来说一下,函数调用时候进行的传参,其实传参和赋值语句一样,也是基于上面两个概念。
Rust 对于赋值语义的扩展
Rust会在该进行内存回收的时候,回收一个变量所申请的内存。
那么什么时候呢?
一个场景就是,这个变量离开他的作用域的时候:
fn main() {
{
let s = String::from("hello world");
}
}
当里面的花括号结束的时候,就是s离开作用域的时候,也是s所指向的内存被回收的时候。
如果在这个作用域内,另搞一个变量s1:
{
let s = String::from("hello world");
let s1 = s;
}
就这么个简单的赋值语句,Rust不仅仅是做了一个浅拷贝。
为了解决作用域结束的时候,两个变量都要进行回收这种bug,Rust规定:
上面的赋值语句,将s里值的所有权转移到了s1中,官方将这种概念称为:move。
- 谁拥有所有权,谁就会被回收
所以上面作用域结束之后,就进行一次回收,灾难解除了。
Rust 函数调用的问题
简单的赋值语句,Rust把所有权move来,move去,这么搞没问题,如果对于函数调用来说,就有点麻烦了:
函数调用的时候,把所有权,move进去, 结果导致函数结束之后,这个值就会被回收。
为了防止这个值不会被回收,函数结束的时候,要把这个玩意return出来,就是再次将所有权,返回到外面。
虽然Rust可以一次性返回多个变量,但是这么搞,就一个字:麻烦!
借而非拿
如果我们把move成为拿的话,拿走了就是别人的。
那么还有一种操作叫做借:
fn main() {
let s = String::from("hello");
let s1 = &s;
let l = string_len(s1);
println!("{} len is {}", s, l)
}
fn string_len(s: &String) -> usize {
s.len()
}
我们来看 string_len 函数, 就是想获取一下 s 这个字符串的长度。
很明显,这个函数不应该在里面把s的内容回收,所以搞了这种语法:&。
这个东西在别的语言里叫取地址,但是在这里叫引用。
上面的s1就是引用了s的内容,但是s1并没有获取到所有权,所以s1传进去之后,函数里面也没有权限回收s的内容。
从头到尾,s对于值的所有权都没有放手交给s1,所以真正释放空间的时机,其实就是main函数结束的时候。
再来看看函数里面,由于s变量声明的时候就带了&这个标记,所以编译器知道,我就是能用s的内容而已,但是不用进行释放。
这种借,官方就是borrow这个词。
如果你出现了编译错误,那么看看具体错误是什么,你会经常看见move borrow这两个词,多看看错误,你就会更加深入的理解Rust了。