Rust变量borrow

2,480 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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了。