《Rust权威指南》 第4章 认识所有权

正是所有权引入,Rust才能够在没有垃圾回收机制的前提下保障内存安全

所有权规则

  • 每一个值都有一个对应的变量作为其“所有者”
  • 在同一时间内,值有且仅有一个所有者
  • 当所有者离开自己的作用域时,其持有的值就会被释放掉

内存与分配:一个基于String类型的例子

字符串类型String,在堆上分配空间。
使用from根据字符串字面量创建一个String实例

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

String是一个可变的文本类型,意味着我们需要在运行时确定其大小,意味着

  • 使用的内存是操作系统在运行时动态分配
  • 当该变量完成其使命后,应该有某种方式将其内存归还给操作系统

不同的编程语言有不同的策略

  • 通常第一步大家都是一样的,提供特定的函数以在堆上为变量分配空间
  • 第二步,在Rust之前有两种作风
    • 垃圾回收机制(Java,Go)
    • 程序员手动释放(C,C++)
  • Rust给出的解决方式是,在变量离开作用域时,自动为其调用drop函数,释放其持有的内存
{
	let s = String::from("hello"); //变量s开始有效
	//执行与s相关的操作
}//s的作用域结束,s生效,调用drop,释放s的内存

变量和数据的交互方式:移动

栈上的数据和堆上的数据是有区别的
对于栈上的数据,比如整型

let x = 5;
let y = x;

其最终效果是,两个值5都被推入栈,变量x,y各绑定一个

而对于堆上的数据

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

首先我们看一个String类型变量的内存布局:一个指向保存字符串内容的指针,一个长度,一个容量——这些储存在栈上;一个字符串——这些储存在堆上

在这里插入图片描述

当进行赋值时,复制了一遍String的数据(注意不是连带字符串一起复制),按理说应该是下面这样

在这里插入图片描述
但是,回顾我们的三大原则,“同一时间内,值有且仅有一个持有者”。上面展示的内存布局有什么问题呢,答案是当s1和s2同时离开作用域时,一块内存就被释放了两遍,这是不可饶恕的错误。所以实际上执行的结果是这样的

在这里插入图片描述

我们说这是s1“移动”到了s2。移动后,s1的内容(指针,长度,容量)现在在s2里,且变量s1不再有效(意味着这个操作之后,试图使用s1这个名称就会报错)

变量和数据的交互方式:克隆

像下面这样,同时复制String栈上和堆上的数据(即所谓的深拷贝)。使用clone()方法

let s1 = String::from("hello");
let s2 = s1.clone();

其结果是
在这里插入图片描述

是否在赋值时发生移动的本质区别:trait

之后我们详细讨论trait,在这里只是提一嘴

  • 拥有Copy这种trait的类型,其变量赋值给其它变量后,仍保可用性
    • 所有整数类型
    • 仅拥有两种值的布尔类型
    • 字符类型
    • 浮点类型
    • 仅仅包含Copy类型的元组
  • 如果类型本身或者其成员实现了Drop这种trait,赋值时就要发生移动

所有权与函数

传参时

将值传递给函数,语义上类似赋值。同理,此时也会产生移动

fn main() {
	let s = String::from("hello"); //s进入作用域
	takes_ownership(s);//发生移动,s所有权转换交给函数的参数
	//s不再有效
}
fn takes_ownership(some_string: String) {//some_string进入作用域
	println!("{}",some_string);
}//some_string离开作用域,调用drop,释放内存

返回值时

同理的,返回值的过程中也会发生移动

fn main() {
	let s = gives_ownership(); //函数的返回值移动到s中
} //s离开作用域,调用drop,释放内存
fn gives_ownership() -> String {
	let some_string = String::from("hello"); //som_string进入作用域
	some_string  //some_string作为返回值移动
}

至此为止,假如希望在调用函数时保留参数的所有权,就不得不让函数返回一个元组,将传入的值作为元组中的一员返回。我们将在下一节讨论另一种做法。

引用与借用

改变上面的例子

fn main() {
	let s1 = String::from("hello");
	takes_ownership(&s1);//注意传入引用
	//s1仍然有效
}//s1离开作用域,调用drop,释放内存
fn takes_ownership(s: &String) { //注意形参声明
	println!("{}",s);
}

有两点改变:在函数的参数声明中,我们使用引用类型&String;在传入参数时,我们传入变量的引用&s1

引用语义下,允许在不拥有值的所有权的情况下使用值。这意味着s根本就没有值的所有权,所以其在离开作用域时不会调用drop释放内存,s1也在函数调用后也仍然保持有效。

这就称为“借用”

在这里插入图片描述

可变引用

fn main() {
	let mut s = String::from("hello");
	change(&mut s);
}
fn change(ss: &mut String) {
	ss.push_str(" world!");
}
  • 注意三个位置都出现了mut,声明变量s,传入可变引用参数,声明函数的参数列表
  • 同一数据,同一作用域下,只允许创建至多一个可变引用。(即不许 let s1 = &mut s;let s2 = &mut s)这是为了在编译时期就避免数据竞争:
    • 多个指针同时访问一个空间
    • 至少一个指针向空间写入数据
    • 没有同步数据访问的机制
  • 不能在拥有不可变引用的时候创建可变引用。这是为了保证不可变引用使用的安全性:使用不可变引用的用户期望值是不可变的,但可变引用可能会改变值

悬垂引用

在Rust中,编译阶段就可以检查悬垂引用的问题,所以通过编译的代码永远不可能出现存在指向已经释放的内存的引用,比如下面的代码

fn dangle() -> &String {
	let s = String:from("s"); //s进入作用域
	&s //返回s的引用
}//s离开作用域,调用drop,释放内存

就产生了一个悬垂引用,不过好在在编译期间就能检查出来

小结:引用的规则

  • 在任何一个给定的时间里,要么只有一个可变引用,要么只有任意数量的不变引用
  • 引用总是有效的

切片

切片允许我们引用集合某一段连续的元素序列,而不是整个集合
切片也是一种引用,遵守引用的规则和借用的规则

字符串切片

类型标识&str,可以如下所示创建:&原String[区间]

let s = String::from("hello world");
let hello = &s[0..5]
let world = &s[6..11]

区间是经典的左闭右开,省略第一个下标表示从头开始,省略第二个下标表示直到结尾

  • 字符串字面量的类型就是切片

  • 可以将字符串切片作为函数参数类型和函数返回值类型。比如下面这个查找字符串中第一个单词的函数

    fn first_word(s:&str) -> &str {
    	let bytes = s.as_bytes();
    	for (i,item) in bytes.iter().enumerate() {
    		if item == b' ' {
    			return &s[0..i];
    		}
    	}
    	&s[..]
    }
    

其他类型切片

以数组为例,切片类型可以是&[i32],创建和使用的语法和字符串切片一样

let a = [1,2,3,4,5];
let slice = &s[1..3];
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Rust权威指南是一本深入介绍Rust编程语言的权威性书籍。这本书主要介绍Rust语言的基本语法和特性,包括Rust的所有关键字、数据类型、函数、模块、宏、”生命周期”、所有权等重要概念的详细解释和使用方法。 这本书由Rust社区知名的开发者Steve Klabnik和Carol Nichols撰写,是Rust开发者及其他想要学习Rust编程语言的程序员的必读之作。书中详细介绍了Rust的语法规则和特性,如何编写高质量、高性能的Rust代码,以及如何使用Rust来进行并发编程和系统级编程。 通过阅读本书,读者能够了解Rust的核心概念,如所有权、生命周期和借用,以及如何使用Rust的工具链进行开发。在本书的后半部分,作者通过实践案例和示例向读者展示了如何使用Rust来编写高效、可靠的程序。 总的来说,Rust权威指南是一本非常实用且详尽的Rust编程语言指南,对于想要学习Rust编程语言的读者非常有益。不仅提供了基础知识方面的介绍,还给读者提供了诸如性能优化等高级内容的指导。同时,本书也适合那些已经有一定Rust编程经验的读者,它们可以通过本书深入了解Rust语言内部之间的联系。 ### 回答2: 《Rust权威指南》是一本针对Rust编程语言的详细讲解和指导的权威指南。该书由Rust的核心开发人员编写,涵盖了Rust语言的基本概念、语法、数据类型、内存管理、并发编程、错误处理、泛型、宏等方面,旨在帮助读者全面深入地学习和理解Rust语言。 该书内容广泛,详细全面,适合初学者和有经验的开发人员。书中每一章都有大量的代码示例,并附有解释和注释,易于理解。书中还介绍了Rust的生态系统和常用开发工具,如包管理器Cargo、测试框架等。 此外,《Rust权威指南》还强调了Rust语言的安全性和性能优势。Rust通过静态类型检查、所有权系统、借用规则等机制,大大减少了程序运行时的内存安全问题。同时,Rust的设计和实现也使得其具有与C++类似的性能优势。 总的来说,《Rust权威指南》是一本权威性强、内容深入的Rust编程指南。对于想要学习Rust编程语言的开发人员来说,这本书绝对是一本值得购买和阅读的好书。 ### 回答3: 《Rust权威指南》是一本全面介绍Rust语言的书籍,该书由Rust中文社区的翻译小组翻译而成。这本书详细讲解了Rust语言的语法、基础知识、数据类型、控制流、函数、模块、生命周期、所有权系统等内容,涵盖了Rust语言的各个方面。 这本书的特点在于其权威性和全面性。作为Rust语言的权威指南,该书不仅涵盖了Rust语言的基础知识,还深入讲解了Rust语言的高级特性,如内存管理、安全性、并发等方面。此外,书中还包括了大量的实例和案例,可以帮助读者更深入地理解Rust语言。 对于初学者来说,该书可以作为Rust语言的入门指南。对于已经掌握了Rust语言基础知识的读者来说,本书也可以作为进一步深入学习和实践Rust语言的指南。此外,该书还提供了完整的API文档和Rust语言的标准规范,方便读者进行参考和查阅。 总之,《Rust权威指南》是一本全面、详细、权威Rust语言指南,适合Rust语言的初学者和进阶者。读者可以通过该书深入了解Rust语言并掌握其应用,提高自己的编程能力和实践经验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值