Rust学习总结之所有权(二)

embedded/2025/2/20 18:37:30/

引用的规则:

  • 在任意给定时间,要么 只能有一个可变引用要么 只能有多个不可变引用
  • 引用必须总是有效的。

下面的引用学习都是围绕上面的两条规则来展开 

一:引用和借用

下面我们通过一个例子展示引用引用并不获取入参的所有权

rust">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()
}

运行结果:

在calculate_length函数的定义中,入参s的类型是&string,& 符号就是 引用,它们允许你使用值但不获取其所有权

变量 s 有效的作用域与函数参数的作用域一样,不过当引用停止使用时并不丢弃它指向的数据,因为我们没有所有权。当函数使用引用而不是实际值作为参数,无需返回值来交还所有权,因为就不曾拥有所有权

我们将创建一个引用的行为称为 借用borrowing)。正如现实生活中,如果一个人拥有某样东西,你可以从他那里借来。当你使用完毕,必须还回去。

引用默认是不可变的,如果尝试要修改引用的变量会报错

rust">fn main() {let s = String::from("hello");change(&s);
}fn change(some_string: &String) {some_string.push_str(", world");
}

运行结果:

那如果我想在函数中修改引用的变量,有没有办法呢

二:可变引用

rust">fn main() {let mut s = String::from("hello");change(&mut s);println!("{}",s);
}fn change(some_string: &mut String) {some_string.push_str(", world");
}

运行结果:

可以看到在函数中修改可变引用也就等同于修改了变量s

不过可变引用有一个很大的限制:在同一时间,只能有一个对某一特定数据的可变引用。尝试创建两个可变引用的代码将会失败:

rust">fn main() {let mut s = String::from("hello");let r1 = &mut s;let r2 = &mut s;println!("{}, {}", r1, r2);
}

运行结果:

我们不能在同一时间多次将 s 作为可变变量借用。第一个可变的借入在 r1 中,并且必须持续到在 println! 中使用它,但是在那个可变引用的创建和它的使用之间,我们又尝试在 r2 中创建另一个可变引用,它借用了与 r1 相同的数据。

防止同一时间对同一数据进行多个可变引用的限制允许可变性,不过是以一种受限制的方式允许。

这个限制的好处是 Rust 可以在编译时就避免数据竞争。数据竞争data race)类似于竞态条件,它由这三个行为造成:

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

注意:以上三个行为同时发生才会造成数据竞争,而不是单一行为。

可以使用大括号来创建一个新的作用域,以允许拥有多个可变引用,只是不能 同时 拥有

rust">let mut s = String::from("hello");{let r1 = &mut s;
} // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用let r2 = &mut s;

类似的规则也存在于同时使用可变与不可变引用

rust">    let mut s = String::from("hello");let r1 = &s; // 没问题let r2 = &s; // 没问题let r3 = &mut s; // 有问题println!("{}, {}, and {}", r1, r2, r3);

不能在拥有不可变引用的同时拥有可变引用

注意一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。例如,因为最后一次使用不可变引用println!),发生在声明可变引用之前,所以如下代码是没有问题的

rust">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 的地方。它们的作用域没有重叠,所以代码是可以编译的。编译器在作用域结束之前判断不再使用的引用的能力被称为非词法作用域生命周期(Non-Lexical Lifetimes,简称 NLL)。

三:悬垂引用

在具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个 悬垂指针dangling pointer),所谓悬垂指针是其指向的内存可能已经被分配给其它持有者。相比之下,在 Rust 中编译器确保引用永远也不会变成悬垂状态:当你拥有一些数据的引用,编译器确保数据不会在其引用之前离开作用域。

下面是一个悬垂引用,Rust 会通过一个编译时错误来避免

下面看看s在内存中的表现

rust">fn dangle() -> &String { // dangle 返回一个字符串的引用let s = String::from("hello"); // s 是一个新字符串&s // 返回字符串 s 的引用
} // 这里 s 离开作用域并被丢弃。其内存被释放。// 危险!

因为 s 是在 dangle 函数内创建的,当 dangle 的代码执行完毕后,s 将被释放。不过我们尝试返回它的引用。这意味着这个引用会指向一个无效的 String


http://www.ppmy.cn/embedded/163147.html

相关文章

【DeepSeek 行业赋能】从金融到医疗:探索 DeepSeek 在垂直领域的无限潜力

网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…

Python的那些事第二十二篇:基于 Python 的 Django 框架在 Web 开发中的应用研究

基于 Python 的 Django 框架在 Web 开发中的应用研究 摘要 Django 是一个基于 Python 的高级 Web 框架,以其开发效率高、安全性和可扩展性强等特点被广泛应用于现代 Web 开发。本文首先介绍了 Django 的基本架构和核心特性,然后通过一个实际的 Web 开发项目案例,展示了 Dj…

Java 设计模式之备忘录模式

文章目录 Java 设计模式之备忘录模式概述UML代码实现 Java 设计模式之备忘录模式 概述 备忘录(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。方便对该对象恢复到原先保存的状态。 UML Originnato…

微信小程序组件间通信与传值的全面解析

微信小程序组件间通信与传值的全面解析 在微信小程序中,组件之间的传值主要通过以下几种方式实现,每种方式都有其适用的场景和数据类型: 1. 父组件向子组件传值(父传子) 父组件可以通过 properties 将数据传递给子组…

大学信息安全技术 期末考试复习题

一、单选题(一) 1、在以下人为的恶意攻击行为中,属于主动攻击的是( )A A.数据篡改及破坏 B.数据窃听 C.数据流分析 D.非法访问 2、数据完整性指的是( &#x…

用deepseek学大模型03-数学基础 概率论 条件概率 全概率公式 贝叶斯定理

要深入浅出地理解条件概率与贝叶斯定理,可以从以下几个方面入手,结合理论知识和实例进行学习: 贝叶斯定理与智能世界的暗语 条件概率,全概率公式与贝叶斯公式的推导,理解和应用 拉普拉斯平滑 贝叶斯解决垃圾邮件分类 …

LVS的NAT及DR模式

DR模式: 原理:负载均衡器接收到客户的请求数据包时,根据调度算法决定将请求发送给哪个后端的真实服务器(RS)。然后负载均衡器就把客户端发送的请求数据包的目标MAC地址改成后端真实服务器的MAC地址(R-MAC&a…

【Elasticsearch】字符过滤器Character Filters

在 Elasticsearch 中,字符过滤器(Character Filters)是文本分析器的重要组成部分,用于在分词之前对原始文本进行预处理。它们可以对字符流进行转换,例如添加、删除或更改字符。Elasticsearch 提供了三种内置的字符过滤…