Rust学习之 所有权理解

Rust所有权准则

js 复制代码
1 Rust 中,每一个值都有一个所有者。
2 任何一个时刻,一个值只有一个所有者。
3 当所有者所在作用域(scope)结束的时候,其管理的值会被一起释放掉。

准则中涉及到两个概念:所有者和作用域。

所谓所有者,在代码里就用变量表示。而变量的作用域,就是变量有效(valid)的那个代码区间。在 Rust 中,一个所有权型变量的作用域,简单来说就是它定义时所在的那个最里层的花括号括起的部分,从变量创建时开始,到花括号结束的地方

作用域代码示例:

js 复制代码
fn main() {    
    let s = String::from("hello");
    // do stuff with s
}  // 变量s的作用域到这里结束

fn main() {    
    let a = 1u32;
    {
        let s = String::from("hello"); 
    }  // 变量s的作用域到这里结束
    // xxxx
    
}  // 变量a的作用域到这里结束

我们在用所有权的概念理解一下下面的代码:

js 复制代码
fn main() {
    let a = 10u32;
    let b = a;
    println!("{a}");
    println!("{b}");
}

在这个例子中,a 具有对值 10u32 的所有权。执行 let b = a 的时候,把值 10u32 复制了一份,b 具有对这个新的 10u32 值的所有权。当 main 函数结束的时候,a、b 两个变量就离开了作用域,其对应的两个 10u32,就都被回收了。这里是栈帧结束,栈帧内存被回收,局部变量位于栈帧中,所以它们所占用的内存就被回收了。

再理解一个字符串的例子:

js 复制代码
fn main() {
    let s1 = String::from("I am a superman.");
    println!("{s1}");
}

局部变量 s1 拥有这个字符串的所有权。s1 的作用域从定义到开始,直到花括号结束。s1(栈帧上的局部变量)离开作用域时,变量 s1 上绑定的内存资源(字符串)就被回收掉了。注意,这里发生的事情是,栈帧中的局部变量离开作用域了,顺带要求堆内存中的字符串资源被回收。之所以能够做到这一点,是因为这个堆中的字符串资源被栈帧中的局部变量所指向了的。

从 Rust 的语法层面看起来,就是变量 s1 对那个字符串拥有所有权。所以 s1 离开作用域的时候,那个资源就一起被回收了。这看起来好像是一个自动的过程,我们并没有像 C 语言中那样,需要手动调用 free() 函数去释放堆中的字符串资源

我们再理解一下字符串引用二次赋值的例子:

js 复制代码
fn main() {
    let s1 = String::from("I am a superman.");
    let s2 = s1;
    //println!("{s1}");
    println!("{s2}");
}

变量 s1 持有这个字符串的所有权。s1 对字符串的所有权从第 2 行定义时开始,到 let s2 = s1 执行后结束。这一行执行后,s2 持有那个字符串的所有权。而此时 s1 处于什么状态呢?处于一种不可用的状态,或者叫无效状态(invalid),这个状态是由 Rust 编译器在编译阶段帮我们管理的,我们只需要从所有权模型去理解它,而不需要操心细节.然后直到花括号结束,s2 及 s2 所拥有的字符串内存,就被回收掉了,s1 所对应的那个局部变量的内存空间也一并被回收了.

用所有权来书写函数:

示例1

js 复制代码
fn foo(s: String) {
    println!("{s}");
}

fn main() {
    let s1 = String::from("I am a superman.");
    foo(s1);
}

这段代码能正常输出。

示例2

js 复制代码
fn foo(s: String) {
    println!("{s}");
}

fn main() {
    let s1 = String::from("I am a superman.");
    foo(s1);
    println!("{s1}");    // 这里加了一行
}

这段代码就会报错,只是加了一行println!("{s1}");报错的原因是说s1的所有权已经被移动到函数里面去了不可以再使用了。也就是foo函数的参数s接受了s1的所有权,再函数结束的时候s就会被回收。

这个例子在其他语言中,一般是不会有问题的。foo 函数也许会修改字符串的值,在外面重新打印的时候,会打印出新的值。

如果我们想继续使用s1的值怎么办,我们可以再foo函数中返回s,代码如下

js 复制代码
fn foo(s: String) -> String {
    println!("{s}");
    s
}

fn main() {
    let s1 = String::from("I am a superman.");
    let s1 = foo(s1);
    println!("{s1}");
}

foo函数返回的是一个新的变量跟原来的不是同一个,这个返回的s1会接管原来s1对应的String的所有权。

相关推荐
DongLi012 天前
rustlings 学习笔记 -- exercises/05_vecs
rust
番茄灭世神3 天前
Rust学习笔记第2篇
rust·编程语言
shimly1234563 天前
(done) 速通 rustlings(20) 错误处理1 --- 不涉及Traits
rust
shimly1234563 天前
(done) 速通 rustlings(19) Option
rust
@atweiwei3 天前
rust所有权机制详解
开发语言·数据结构·后端·rust·内存·所有权
shimly1234563 天前
(done) 速通 rustlings(24) 错误处理2 --- 涉及Traits
rust
shimly1234563 天前
(done) 速通 rustlings(23) 特性 Traits
rust
shimly1234563 天前
(done) 速通 rustlings(17) 哈希表
rust
shimly1234563 天前
(done) 速通 rustlings(15) 字符串
rust
shimly1234563 天前
(done) 速通 rustlings(22) 泛型
rust