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的所有权。

相关推荐
1nv1s1ble11 小时前
记录rust滥用lazy_static导致的一个bug
算法·rust·bug
华科云商xiao徐16 小时前
用Rust如何构建高性能爬虫
爬虫·rust
景天科技苑18 小时前
【Rust UDP编程】rust udp编程方法解析与应用实战
开发语言·rust·udp·udp编程·rust udp
火柴就是我1 天前
Rust 学习之变量的可变与不可变
rust
维维酱1 天前
Rust - Futures
rust
Pomelo_刘金2 天前
Rust: 1.86.0 新版本发布。
rust
Pomelo_刘金2 天前
发布 Rust 1.87.0 —— 以及 Rust 十周年!
rust
susnm2 天前
你的第一个组件
rust·全栈
该用户已不存在2 天前
OpenAI 用 Rust 重写 AI 工具,你的本地开发环境跟上了吗?
前端·javascript·rust