Rust之所有权(二):引用和借用

五、引用与借用 (References & Borrowing) Rust的引用与借用系统通过严格的编译时规则确保内存安全。该系统允许访问值而不获取所有权,遵循核心原则:任意时刻只能存在一个可变引用或多个不可变引用,且引用必须始终有效。Rust防止数据竞争和悬垂引用,通过借用检查器验证引用的有效性和作用域。引用作为零成本抽象,具有与裸指针相同的性能但更安全。最佳实践包括优先使用不可变引用、缩小可变引用作用域、利用非词法生命周期(NLL)优化代码结构。这一机制为Rust提供了安全的内存访问、无数据竞争的并发保证,同时保持了高性能。 阅读详情

开发环境

  • Windows 10
  • Rust 1.55.0

 

  • VS Code 1.60.2

 

项目工程

这里继续沿用上次工程rust-demo

 引用和借用

上述章节中的元组代码的问题是,我们必须将String返回给调用函数,这样我们仍然可以在调用之后使用String来calculate_length,因为String被移动到计calculate_length中。

下面是如何定义和使用具有对对象的引用作为参数的calculate_length函数,而不是获取该值的所有权:

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

首先,注意变量声明和函数返回值中的所有元组代码都不见了。其次,请注意,我们将&s1传递到calculate_length中,在其定义中,我们采用&string而不是String。

这些符号是引用,它们允许您引用某些值,而不需要拥有它的所有权。指向String s1的&String s的关系图如下:

 注意:

与使用&的引用相反的是取消引用,这是通过取消引用操作符*完成的。

 让我们仔细看看这里的函数调用

let s1 = String::from("hello");

let len = calculate_length(&s1);   // 引用

&s1语法允许我们创建引用,引用s1的值,但不拥有它。因为它不拥有它,所以当引用超出范围时,它所指向的值不会被删除。

同样,函数的签名使用&指示参数s的类型是引用。让我们添加一些解释性注释:

fn calculate_length(s: &String) -> usize { // s是字符串引用
    s.len()
} // 这里,s超出了范围。 但是因为它对它所指代的东西没有所有权,所以什么都不会发生。

变量s有效的作用域与任何函数参数的作用域相同,但当它超出作用域时,我们不会删除引用指向的值,因为我们没有所有权。当函数将引用作为参数而不是实际值时,我们将不需要返回值来返回所有权,因为我们从来没有所有权。

我们称有引用为函数参数借用。在现实生活中,如果一个人拥有某物,你可以向他们借。当你完成的时候,你必须把它还给我。

那么,如果我们试图修改我们正在借来的东西,会发生什么呢?尝试上例中的代码。出现警报:它不起作用!

fn main() {
    let s = String::from("hello");

    change(&s);
}

fn change(some_string: &String) {
    some_string.push_str(", world");
}

运行测试

 正如变量在默认情况下是不可变的一样,引用也是不可变的。我们不允许修改我们所引用的东西。

可变引用

我们只需稍作调整,就可以修复上述示例中的代码的错误。

fn main() {
    let mut s = String::from("hello");

    change(&mut s);
}

fn change(some_string: &mut String) {    // 关键字mut
    some_string.push_str(", world");
}

首先,我们必须改变s的类型为mut。然后,我们必须使用&mut s创建一个可变引用,并接受some_string:&mut字符串的可变引用。

但是可变引用有一个很大的限制:对特定范围内的特定数据只能有一个可变的引用。下面的代码会出错:

fn main() {
    let mut s = String::from("hello");

    let r1 = &mut s;      // r1引用可变字符串s
    let r2 = &mut s;      // r2引用可变字符串s
 
    println!("{}, {}", r1, r2);
}

运行测试 

 这一限制允许可变性,但以一种非常可控的方式。这是新Rust程序员们苦苦挣扎的地方,因为大多数语言都允许你随时变异。

拥有这一限制的好处是,Rust可以在编译时防止数据竞争。数据竞争类似于竞争条件,并在这三种行为发生时发生。

  • 两个或多个指针同时访问相同的数据。
  • 至少有一个指针被用来写入数据。
  • 没有用于同步数据访问的机制。

数据竞赛会导致未定义的行为,当您试图在运行时跟踪它们时,很难诊断和修复它们;Rust防止了这个问题的发生,因为它甚至不会用数据竞争来编译代码!

与往常一样,我们可以使用花括号创建一个新的范围,允许多个可变引用,而不是同时引用:

fn main() {
    let mut s = String::from("hello");

    {
        let r1 = &mut s;
    } // r1超出了这里的范围,所以我们可以在没有问题的情况下做一个新的参考

    let r2 = &mut s;
}

对于组合可变的和不可变的引用,也存在类似的规则。参考如下示例:

fn main() {
    let mut s = String::from("hello");

    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    let r3 = &mut s; // 这里会出错

    println!("{}, {}, and {}", r1, r2, r3);
}

 运行测试

 当我们有一个不变的引用时,我们也不能有一个可变的引用。不可变引用的用户不会期望值突然从它们下面改变!但是,多个不可变的引用是可以的,因为任何刚刚读取数据的人都没有能力影响其他人对数据的读取。

请注意,引用的作用域从引入它的位置开始,并一直持续到上次使用该引用。例如,这段代码将编译,因为最后一次使用不可变引用发生在引入可变引用之前。

fn main() {
    let mut s = String::from("hello");

    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    println!("{} and {}", r1, r2); // 变量r1和r2在此之后将不再使用

    let r3 = &mut s; // 没问题
    println!("{}", r3);
}

运行测试 

 不可变引用的作用域r1和r2在println!之后结束!最后使用它们的位置,这是在创建可变引用r3之前。这些作用域不重叠,因此允许使用此代码。

尽管借用错误有时可能令人沮丧,但请记住,是Rust编译器及早指出了潜在的错误(在编译时而不是在运行时),并向您展示了问题所在。然后你就不需要找出为什么你的数据不是你想象的那样了。

悬空引用

在有指针的语言中,很容易错误地创建一个悬空指针,该指针通过释放一些内存,同时保留指向该内存的指针,来引用内存中可能给其他人的位置。相比之下,在Rust中,编译器保证引用永远不会是悬空引用:如果您有对某些数据的引用,编译器将确保数据在引用数据之前不会超出范围。 

让我们尝试创建一个悬空引用,Rust将通过编译时错误来防止这种引用。 

fn main() {
    let reference_to_nothing = dangle();
}

fn dangle() -> &String {    // dangle返回对字符串的引用。
    let s = String::from("hello"); // s是个新字符串

    &s                          // 返回对字符串s的引用
}                               // s超出了范围,被丢弃了。它的内存信息消失了。

运行测试

此错误消息引用了我们尚未讨论的一个特性:lifetime。我们将在后面论lifetime。

因为s是在dangle中创建的,所以当dangle完成时,s将被释放。但我们试图返回一个引用。这意味着此引用将指向无效String。

这里的解决方案是直接返回String。如下

fn main() {
    let string = no_dangle();
}

fn no_dangle() -> String {                 // 返回类型为字符串
    let s = String::from("hello");

    s                                      // 直接返回字符串
}

这没有任何问题。所有权被移除,任何东西都不会被释放。

引用规则

  • 在任何给定的时间,您可以有一个可变引用或任意数量的不可变引用。
  • 引用必须始终有效。

本章重点

  • 引用
  • 借用
  • 可变引用
  • 引用的规则和注意事项
从零到可发布:用Rust和eGUI Panel布局打包一个跨平台设置窗口(附完整代码) 本文详细介绍了如何使用Rust和eGUI框架从零开始构建一个跨平台的设置窗口应用。通过实战指南,包括项目初始化、Panel布局设计、响应式界面实现、设置项具体编码,以及最终的跨平台打包发布流程,帮助开发者掌握Rust图形界面开发的核心技术。文章特别强调了eGUI的Panel布局系统在创建专业级设置界面中的应用,并提供了完整的代码示例和性能优化建议。 阅读详情

相关推荐

深入理解Rust引用与借用

获取变量的引用,称之为借用(borrowing)。

Coder_0001的博客 1966

Rust 可变引用与不可变引用

如何在一个作用域里使用同一个可变变量的不可变引用与可变引用 fn mut_and_not_mut(){ let mut s = String::from("Hello Wrold!"); let s1 = &s; let s2 = &s; println!("s1:{}\ts2:{}\n",s1,s2); //就算声明的不可变引用没有被调用,只要确保下面声明完可变引用后不会再调用上面的不可变引用即可。不过较少发生这种声明完一个变量又不用的情况。 let

muyouking 1355

【Rust所有权机制】Rust所有权机制详细解析与应用实战

所有权(系统)是 Rust 最独特的功能,其令 Rust 无需垃圾回收(garbage collector)即可保障内存安全。 因此,理解Rust 中所有权如何工作是十分重要的。本文我们将讲到所有权以及相关功能:借用、slice 以及 Rust 如何在内存中布局数据。

景天科技苑 6万+

Rust学习笔记(6)——引用情况struct中所有权的转移问题

还是先定义struct: #[derive(Debug)] struct Point { x: Box<i32>, y: Box<i32>, z: i32, } 通过可变引用,可以改变成员的值,与结构体成员为简单的i32类型一致,除了不能对Box类型使用 “+=”符号以外。 fn main() { let mut a = Point { x: Box::new(3), y: Box::new(4),

Konquerx的博客 2566

翻倍密码系统主图指标-行情买卖以及持股-翻倍密码买点-获利了结,通达信炒股软件指标公式

华森制药,上图案例走势,低位出现“开始潜伏”信号,进场红色K线持股待涨,进入“翻倍密码买点”,开启紫色K线的持续上涨,目前也达到了翻倍行情,还没出现“获利了结”的出场信号。这套操盘系统,即便出现信号没走出翻倍行情,也都是可以全身而退,获利出局,或者微微止损出场,并不代表每一个波段都能翻倍,但是走翻倍行情,你按照信号参与或持仓是不会错过的。用这个操盘系统操作,你也不需要天天的盘中看盘,不需要担心盘中的震荡,只要趋势不变,哪怕是偶尔出现跌停,也不影响行情的继续上涨,这都是上涨途中的正常调整。

weixin_69760830的博客 3679

K型热电偶负电压采集难题?3种运放电路设计方案对比(含Multisim仿真)

本文针对K型热电偶在低温测量中产生的负电压采集难题,详细对比了三种运放电路设计方案,包括双电源运放反相放大、单电源仪表放大器和差分放大电路,并结合Multisim仿真数据分析了各方案的性能差异。文章还提供了PCB布局和抗干扰设计的实战技巧,帮助工程师解决热电偶信号采集中的核心挑战。

postgres8guard的博客 1017

C++ | 引用和常引用 | 引用和常引用的区别

目录 引用 常引用 引用 引用是一种更安全的指针,因为引用在底层是以指针的方式处理的,针对于引用变量使用的地方,系统自带解引用过程,也就是说引用变量占有内存空间,即在下面这个例子中,b中存放的是a的地址。 #include<iostream> int main() { int a = 10; int &b = a; return 0; } 使用引用时需要注意的事项 引用一定要初始化 引用是不能改变的,例如,上面b已经是a的别名,所以b不能成为c

ThinPikachu的博客 1854

“傻瓜”stata入门——熟悉操作界面

小白开始学习stata和matlab,零基础上手

nnpersistent的博客 4454

Rust中的‘static生命周期:从常量到契约的深度解读

'static是Rust类型系统中一个双重含义的关键字,它既是“永恒”的引用生命周期,也是“自包含”的类型约束。&'static T(引用):一个确保在程序结束前都有效的引用。它指向的数据可以是在编译期确定的(字面量),也可以是在运行时“泄漏”的(Box::leakT: 'static(约束):一个类型安全契约,保证该类型不包含任何“临时”的借用。这是实现安全并发()和动态类型(dyn Any)的基石,它强制数据要么被拥有,要么被永久借用。理解'static。

m0_73236311的博客 795

调试GMS应用,报错“此设备未获得play保护机制认证”问题解决

不少同学在调试GMS相关应用时,需登录Google账号,有时会弹出如下通知。Google登录界面也会出现如下提示这个报错的原因是设备未通过Google认证,google服务器未配置荣耀设备的型号白名单导致国内网页有一些指导方法在鸿蒙\荣耀的设备上消除这个提示,但本质的问题没有解决,无法登录Google账号。

胡同老道的博客 2万+

WPS占用C盘空间解决方案与优化技巧

办公软件在长期使用过程中会产生大量缓存和历史文件,这些文件往往隐藏在系统目录中,导致磁盘空间被无声占用。以WPS为例,其版本历史备份、云同步缓存和模板库是主要空间占用源。通过分析文件存储机制,可以定位到具体的缓存路径,如AppData目录下的Kingsoft文件夹。针对这一问题,有效的解决方案包括手动清理、自动化脚本和注册表优化。对于需要持续监控的场景,可以结合PowerShell脚本和任务计划实现自动化管理。在实际应用中,这些技术不仅适用于WPS,也可迁移到其他办公软件的空间优化中,是每位IT运维人员和高

weixin_30788239的博客 455

C++与Rust内存安全深度对比:从核心原理到工程实践

内存安全是系统编程的基石,它关乎程序的稳定性和安全性。其核心原理在于通过编译时或运行时的机制,防止缓冲区溢出、空指针解引用、释放后使用等常见内存错误。从技术价值看,内存安全能显著减少软件漏洞,提升系统可靠性,降低维护成本。在应用场景上,无论是操作系统、浏览器引擎,还是网络服务、嵌入式系统,对内存安全都有极高要求。本文围绕C++之父Bjarne Stroustrup引发的讨论,深入剖析了现代C++通过智能指针、RAII和核心指南构建的‘可达安全’模型,以及Rust通过所有权、借用检查在编译期强制实现的‘默认安

weixin_33720078的博客 429

Oracle-如何修复undo坏快

undo出现坏快,导致数据库宕机,无法启动

qqqyyy0422的博客 570

Rust 中的引用与借用

所有权、借用和 slice 这些概念让 Rust 程序在编译时确保内存安全。Rust 语言提供了跟其他系统编程语言相同的方式来控制你使用的内存,但拥有数据所有者在离开作用域后自动清除其数据的功能意味着你无须额外编写和调试相关的控制代码。

专注于大数据方向,区块链,前后端,数据可视化,人工智能等领域 3320

在 SAP 中,寄售业务(Consignment)与管道业务(Pipeline)都属于“物料先到、发票后到”的特殊采购模式,但两者的库存形态、物权转移时点、结算触发条件、系统配置完全不同

SAP中寄售业务与管道业务均属"先货后票"采购模式,但存在本质差异。寄售业务(如手机芯片)采用实物库存管理(标记"K"),物权在领用时转移,通过411K/201K移动类型触发负债;管道业务(如蒸汽)无实物库存,采用261P等移动类型直接消耗并生成应付。系统配置上,寄售需K类信息记录及GR收货,管道需P类信息记录且免GR。两者均通过MRKO汇总结算,但寄售按领用量计费,管道按提取量实时计费。核心区别在于库存形态与物权转移时点。

金牌架构师 556

Python 爬虫实战:知网论文数据爬取(题录 / 摘要 / 引用提取 + Excel 结构化)

在学术研究中,获取准确且全面的文献数据是至关重要的。知网(CNKI)作为国内最大的学术文献数据库,拥有海量的论文资源。通过 Python 爬虫技术,我们可以高效地从知网爬取论文数据,包括题录、摘要和引用信息,并将这些数据结构化存储到 Excel 文件中,方便后续的学术分析和研究。

yansideyucsdn的博客 1748

rust学习之所有权,引用和借用

所有权是Rust独有的概念,是一种资源管理机制,主要是针对堆内存管理的,栈也没啥好管理的,在编译时以及程序运行时自动完成,不需要人为干预,而堆就不一样了,拿C语言来说,其最基本的申请堆内存的函数malloc,申请的内存用完以后都要使用free及时释放,要不然就会造成大名鼎鼎的内存泄漏,但是我觉得应该有很多很多人在初学的时候总是忘记释放,而且由于堆内存的特殊性,像汽车这种对安全性要求比较高的行业,基本上都是禁止使用诸如mallocRequiredC90, C99。

weixin_42932596的博客 1186

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Rust 所有权 借用与引用

Rust的所有权、引用和借用机制提供了一种强大而安全的内存管理方式。通过这些机制,Rust能够在编译时避免许多常见的内存错误,如悬垂引用和数据竞争。尽管这些规则可能在开始时显得复杂,但它们为你提供了一个强大的工具集来编写安全、并发的代码。Rust的所有权系统是其安全性和性能的核心,它通过一套严格的规则来管理内存使用,防止数据竞争和悬垂引用。本文将详细探讨Rust中的引用、借用及其规则,并讨论如何利用这些特性来编写健壮的代码。借用是Rust中的一个重要概念,它允许函数在不获取所有权的情况下使用值。

HHX_01的博客 1793

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Rust核心内存安全机制——所有权、借用与生命周期

Rust核心内存安全机制解析 Rust通过所有权、借用和生命周期三大机制保障内存安全。所有权机制规定每个值有唯一所有者,离开作用域自动释放,可通过转移(Move)或克隆(Clone)操作。借用机制包括不可变引用(&T)和可变引用(&mut T),遵循严格的引用规则避免数据竞争。生命周期参数('a)标注引用有效范围,防止悬垂引用,需在函数和结构体中显式声明。这些机制共同实现了无GC的高效内存管理,解决了传统系统编程中的内存安全问题。

编程世界如山海,探索不止。 1万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

RUST所有权和借用

所有权和借用是 Rust 中保证内存安全的重要机制。所有权:每个值都有唯一的所有者,所有权可以转移。借用:通过不可变引用或可变引用来借用数据,避免转移所有权。借用规则:在同一作用域中,允许多个不可变引用,但最多只能有一个可变引用。理解所有权和借用将帮助你在编写 Rust 代码时避免内存错误,并充分利用 Rust 编译器的强大功能来捕获潜在的内存问题。

aliancn的收藏地 1165

Rust所有权与借用规则深度解析:从踩坑到理解

刚开始学Rust的时候,我对所有权的概念完全懵圈。什么是所有权?为什么要有所有权?这跟我之前学的其他语言完全不一样啊!所有权是Rust用来管理内存的一套规则,不需要垃圾回收器就能保证内存安全。Rust的所有权和借用规则虽然一开始很难理解,但它们是Rust安全性和性能的关键。通过本文的介绍,希望能帮助大家对这些概念有更清晰的认识。保持学习,保持输出!今天终于搞懂了所有权,哭死!向大佬们低头学习!

no1coder的博客 1万+

掌握Rust语言核心:所有权、借用与生命周期

Rust语言以所有权、借用和生命周期为核心特性,提供安全、高效的内存管理。所有权确保数据唯一性,避免内存泄漏;借用允许非所有者使用数据,优化内存使用;生命周期解决悬挂引用问题,保障引用安全。掌握这些概念,有助于编写高质量的Rust程序。

silenceallat的博客 1596

【Rust中的所有权(引用与借用)】

所有权是指所有权人依法对自己的财产享有的占有使用收益和处分的权利,所有权是Rust一系列管理内存的规则。通过这两组定义可以更通俗的理解到什么是rust的所有权,Rust即是“所有权人”,程序内存即是Rust的“财产”,Rust对内存独享的管理能力即是对其财产进行占有和使用出借等的权力。这里要特别强调的是,真正管理内存的是Rust内存规则本身,而不是具体某一个变量或载体,而Rust中的各种针对内存的写法规则也都是为了匹配Rust的内存处置规则。

m0_37719524的博客 1224

RUST所有权与借用

所有权与借用引用

m0_52526873的博客 2279

Rust 引用与借用

我们可以为 String 值提供一个引用。引用类似于指针,它是一个地址,可以通过它访问存储在该地址的数据;这些数据归其他变量所有。与指针不同的是,引用保证在其生命周期内始终指向某种类型的有效值。

Humbunklung的专栏 1438

Rust的所有权和借用

什么样子的数据才会有所有权Rust中数据可以分为基本类型和非基本类型。可以通过以下两个方面来理解这些东西的所有权。就不会发生所有权的转化。也就是说所有权是针对于堆上分配的来说的。所有权转移是怎么样的每一个变量都是有作用域的。并且在离开这个作用域的时候,会调用Drop方法来释放这个变量所指代的内存。那么在这个作用域中,如果内存的所属权从一个变量转移到了另外一个变量。那就是发生了所有权的转移。所以,这也得出了以下的先决条件。这三条是所有权的规则。那在实际发生所有权转移的时候,内存中发生了什么事情呢。

qq_43009242的博客 1029
上一篇: Rust之所有权(一):所有权
下一篇: Rust之所有权(三):切片类型
枫★曦
博客等级 码龄19年 251粉丝 153原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值