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

相关推荐
doiito(Do It Together)9 小时前
我用 Rust 写了个 AI 媒体管家:Gliding Horse 赋能 media_agent,目标是让 ComfyUI 工作流彻底自动化
人工智能·架构·rust·媒体
独孤留白14 小时前
从C到Rust:告别 C 的"指针 + 长度"手动模式
前端·rust
咸甜适中14 小时前
rust语言学习笔记(指针十一)Cow<T>(写时克隆)
笔记·学习·rust
doiito15 小时前
【Agent Harness】 给 ComfyUI 装上一个 Rust 大脑:media_agent 架构深度揭秘
ai·rust·架构设计·系统设计·ai agent
花褪残红青杏小2 天前
Rust图像处理第11节-故障风 RGB 通道偏移:错位错色制造电子故障
rust·webassembly·图形学
花褪残红青杏小2 天前
Rust图像处理第10节-浮雕/雕刻滤镜:邻域差值生成凹凸效果
rust·webassembly·图形学
Rockbean2 天前
10分钟Solana-性能web3-2.4 Rust 编程基础三:结构体、枚举、错误处理与集合
rust·web3·智能合约
doiito2 天前
【Agent Harness】Gliding Horse 上下文感知与智能压缩:让 Agent 的“注意力”永不偏移
ai·rust·架构设计·系统设计·ai agent