Rust认识所有权(4)

2023-09-18 10:31:32

1.认识所有权

  • 所有权(系统)是Rust最为与众不同的特性,它让Rust无需垃圾回收器,即可以保证内存安全。

2.什么是所有权?

2.1程序运行管理运行的方式

  • 一些语言中具有垃圾回收机制,在程序运行时不断地寻找不再使用的内存
  • 一些语言中,开发者必须亲自分配释放内存
  • Rust中,通过所有权系统管理内存编译器在编译时会更加一些列规则进行检查;在运行时,所有权系统的任何功能都不会减慢程序

2.2栈(Stack)和堆(Heap)

  • 栈和堆都是代码在运行时可供使用的内存
1.栈(Stack)
  • 栈以放入值的顺序存储值并以宪法顺序取出值,也称为先进后出
  • 栈中的所有数据都必须占用已知固定的大小
  • 入栈比堆上分配内存要快,入栈时分配器无需为存储新数据去搜索内存空间,其位置总是在栈顶
2.堆(Heap)
  • 在编译时大小未知或大小可能变化的数据,要改为存储在堆上
  • 堆时缺乏组织的,当向堆放入数据时,需要请求一定大小的空间
  • 内存分配器在堆的某处知道到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的指针
  • 访问堆上的数据比访问栈上的数据慢,必须通过指针来访问

2.3所有权规则

  • Rust中的每一个值都有一个被称为其所有者的变量
  • 值在任意时刻有且只有一个所有者
  • 当所有者离开作用域,这个值将被丢弃

2.4变量作用域

  • 作用域是一个项(item)在程序中的有效范围
fn main(){						//str在这里无效,它尚未声明
	let str = "生活很美好";		//从此处起,str开始有效
	
}								//此作用域已经结束,str不再有效

2.4String类型

  • let str = "hello"let str = String::from("hello")的区别
    • let str = "hello"
      • 这种方式创建的字符串是静态分配的,存储在程序可执行文件中,并且在运行时不可变
      • 优势
        • 性能高效:因为字符串时静态分配的,不需要额外的内存分配和释放操作
        • 编译时检查:编译器可以在编译时检查字符串的有效性,避免一些运行时错误
    • let str = String::from("hello")
      • 优势
        • 动态大小: 可以根据需要动态增加或减少字符串的长度
        • 可变性: 可以修改字符串的内容
        • 动态分配: 适用于需要在运行时构建字符串的情况
fn main(){
	let str = "hello";
	
	let mut str1 = String::from("hello");
	str1.push_str(",Tom");
	println!("{}",str1);// 输出 hello,Tom

}

2.5内存与分配

  • 针对字符串字面量,在编译时就知道内容,文本被直接硬编码进最终的可执行文件中,快速且高效,但是字符串字面量不可变
  • 对于String类型,为了支持一个可变,可增长的文本片段,需要在堆上分配一块在编译时未知大小的内存来存放内容
    • 必须在运行时向内存分配器请求内存
    • 需要一个当我们处理完String时将内存返回给分配器的方法
1.以String类型为参考
  • 当调用String::from(" "),它的实现请求其所需的内存
  • 释放内存
    • 有垃圾回收机制的语言中,GC记录并清除不再使用的内存
    • 没有GC的话,识别除不再使用的内存并调用代码显式释放
    • Rust采用了一个不同的策略: 内存在拥有它的变量离开作用域后,就被自动释放
    /*
    	当变量离开作用域,rust为我们调用一个特殊函数,这函数叫做drop自动释放内存
    */
    fn main(){
    	let str = String::from("hello");	//从此起,str开始有效
    	//使用str
    	
    }										//此作用域已结束,str不在有效
    
2.变量与数据交互的方式(一): 移动
  • 在rust中,多个变量可以采取不同的方式与同一数据进行交互
let x = 20;
let y = x;
  • 将20绑定到x,接着生成一个值x的拷贝并绑定到y,变量 x 和 y 都等于20
  • 整数是已知固定大小的简单值,两个20都被放入栈中
2.1String版本
let str = String::from("hello");
let str1 = str;

在这里插入图片描述

  • 一个指向存放字符串内容内存的指针一个长度一个容量,这一组数据存放在栈上
  • 右侧则是堆上存放的内容的内存部分
  • 并且str赋值给str1,只拷贝了它的指针、长度和容量,并没复制指针指向的堆上数据
fn main() {
    let str = String::from("自强不息");
    let str1 = str;
    // println!("{}", str);// error
}

  • 两个数据指针指向同一个位置,有一个内存安全问题
    • strstr1 离开作用域,它们都会尝试释放相同的内存,两次释放相同内存会导致内存污染
    • 为了确保内存安全,在执行let str1 = str;之后,Rust认为str不再有效
3.变量与数据交互的方式:克隆
  • 需要深度复制String中堆上的数据,而不仅仅是栈上的数据
  • 需要使用到一个clone的通用函数
fn main() {
    let str = String::from("自强不息");
    let str1 = str.clone();
    println!("str = {},str1 = {}", str, str1); // str = 自强不息,str1 = 自强不息
}
  • 非必要时,不要轻易使用这种方式,会消耗代码的资源
4.只在栈上的数据: 拷贝
  • rust中,存放在栈中的数据,拷贝其实际的值是最快速的。

2.5所有权与函数

fn main() {
    let str = String::from("自强不息"); //str进入作用域
    takes_ownership(s); // str的值移动到函数里面,所以这里不再有效

    let num = 20; // num进入作用域
    makes_copy(20); // num移动函数里面,但i32是拷贝,所以在后面可继续使用x
} // num移除作用域,str的值已被移走
fn takes_ownership(some_string: String) {
    // some_thing 进入作用域
    println!("{}", some_string);
} // some_thing移除作用域并调用 drop 方法
fn makes_copy(some_interger: i32) {
    // some_integer进入作用域
    println!("{}", some_interger);
} // some_integer移除作用域

2.6返回值与作用域

fn main() {
    let str = String::from("自强不息");
    let (str1, len) = calculate_length(str);
    println!("The length of {} is {}", str1, len); // The length of 自强不息 is 12
}
fn calculate_length(str: String) -> (String, usize) {
    let length = str.len();
    (str, length)
}

3.引用与借用

  • 引用像一个指针,它是一个地址
  • 访问储存于该地址的属于其他变量的数据,与指针不同,引用确保指向某个特定类型的有效值
    在这里插入图片描述
fn main() {
    let str = String::from("自强不息");
    let len = calculate_length(&str);
    println!("The length of {} is {}", str, len); // The length of 自强不息 is 12
}
fn calculate_length(str: &String) -> usize {
    let length = str.len();
    length
}

  • &str 语法让我们创建一个指向值str的引用,但是并不拥有它,所以当引用停止使用时,它所指向的值也不会被丢弃

  • 创建一个引用的行为称为借用,只有使用权限,没有拥有权限借用变量不可修改

3.1可变引用

  • 允许修改一个借用的值,就是可变引用
fn main() {
    let mut str = String::from("天行健,君子以");
    change(&mut str);
    println!("{}", str)
}
fn change(some_thing: &mut String) {
    some_thing.push_str("自强不息!");
}
1.可变引用的限制
  • 创建一个可变引用,我们就不能再创建对变量的引用
fn main() {
    let mut str = String::from("天行健,君子以");
    let str1 = &mut str;
    // let str2 = &mut str; //error
    // println!("{},{}", str1, str2);
}
  • 防止同一时间对同一数据存在多个可变引用,限制的好处就是Rust可以再编译时就避免数据竞争,数据竞争类似于竞态条件,它由以下行为造成
    • 两个或更多指针同时访问同一数据
    • 至少有一个指针被用来写入数据
    • 没有同步数据访问的机制
1.1允许多个可变引用-作用域
fn main(){
	let mut str = String::from("自强不息");
	{
		let str1 = &mut str;
	}
	let str2 = &mut str;

}

1.2不可变引用和可变引用
  • 不能在拥有不可变引用的同时拥有可变引用
  • 多个不可变引用是可以的
  • 不可变引用和可变引用同时存在的场景
    • 不可变引用str1str2的作用域在println!最后一次使用之后结束
    • 可变引用str3创建和str1 & str2作用域没重叠
fn main(){
	let mut str = String::from("自强不息");
	let str1 = & str;
	let str2 = & str;
	println!("{},{}",str1,str2);// 自强不息,自强不息
	let str3 = &mut str;
	println!("{}",str3);//自强不息
}

3.2悬垂引用

  • 具有指针的语言中,很容易通过释放内存时保留指向它的指针而错误地生成一个垂悬指针
  • 垂悬指针是其指向的内存可能已经被分配给其他持有者
  • 在rust中,编译器确保引用永远不会变成垂悬指针,编译器确保数据不会在其引用之前离开作用域
fn main(){
	let reference_to_nothing = dangle();

}

/*
 // error
fn dangle() -> &String{
	let str = String::from("自强不息");
	&str //返回字符串str的引用
}// 这里str离开作用域并被丢弃,其内存释放

*/

fn dangle() ->String{
	let str = Stirng::from("自强不息");
	str
}

4.Slice类型

fn main() {
    let mut str = String::from("hello world");
    let word = first_word(&str);
    println!("{}", word); // 5
    str.clear();
}
fn first_word(str: &String) -> usize {
    let bytes = str.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        // b' ',表示字节字面量,它表示空格字符
        if item == b' ' {
            return i;
        }
    }
    str.len()
}

4.1字符串slice

  • 字符串sliceString中一部分值的引用
let str = String::from("hello world");
let hello = &str[0..5];
let world = &str[6..11];
println!("{}",world);//world

在这里插入图片描述

fn main() {
    let mut str = String::from("hello world");
    let hello = first_word(&str[..]);
    //str.clear();// clear需要清空String,尝试获取一个可变引用,而hello使用了不可变引用
    println!("The value of is {}", hello);
}
fn first_word(str: &str) -> &str {
    let bytes = str.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &str[0..i];
        }
    }
    &str[..]
}

4.1字符串字面值就是slice
  • 字符串字面值被存储在二进制文件中
let str = "hello world";
  • 这里的str类型就是&str,它指向二进制程序特定位置的slice
4.2字符串slice作为参数
fn first_world(st: &str) -> &str{}

4.2其他类型的slice

let arr = [1,2,5,1,6,8];
let slice = &arr[1..3];
assert_eq!(slice,&[2,5]);

  • slice的类型是&[i32]
更多推荐

【Linux】基础IO

文章目录一.C语言文件IO1.C语言文件接口汇总2.什么是当前路径?2.默认打开的三个流二.系统文件I/O1.open(1)open的第一个参数(2)open的第二个参数(3)open的第三个参数(4)open的返回值2.close3.write4.read三.文件描述符fd四.文件描述符的分配规则五.重定向1.重定向

哪些企业需要数字化转型?

数字化转型是一个广泛且持续的过程,可以使各行业的公司受益。虽然数字化转型的具体需求和目标可能因企业而异,但这通常是保持竞争力和相关性的必要条件。以下是一些可能需要数字化转型的公司和行业的一些示例:1.传统零售商:零售商需要适应电子商务和全渠道客户体验,以与亚马逊等在线巨头竞争。2.金融机构:银行和金融机构需要采用数字技

通用商城项目(下)之——Nginx的安装及使用

(作为通用商城项目的一个部分,单独抽离了出来。查看完整见父页面:)加入Nginx-完成反向代理、负载均衡和动静分离1.配置SSH-使用账号密码,远程登录Linux1.1配置实现1、配置sshd1)sudovi/etc/ssh/sshd_config2)将PasswordAuthentication的no改成yes3)重

WebDAV之π-Disk派盘 + BubbleUPnP

BubbleUPnP是一款功能强大的Android播放器,支持UPnP/DLNA多屏互动。它可以将手机内容投屏到电视大屏上,与家人和朋友一起共享。此外,BubbleUPnP还提供了丰富的音乐和影视资源,您可以在线搜索并播放喜欢的内容。以下是BubbleUPnP的一些主要特点:1.支持Chromecast和转码:Bubb

conda init 导致的 powershell 启动缓慢的问题(Loading personal and system profiles took xxxx ms.)

文章目录一、问题描述二、问题溯源三、解决方案3.1测试3.2方案一:不在powershell中使用conda3.2方案二:需要时再在powershell中使用conda(推荐)四、powershell7特点一、问题描述powershell启动缓慢:每次启动都会加载很久的配置文件:Loadingpersonalandsy

【Python】conda虚拟环境下使用pyinstaller打包程序为exe

文章目录一、为什么要用conda虚拟环境二、pyinstaller用法2.1安装PyInstaller2.2基本用法打包一个Python脚本2.21打包一个Python项目2.22打包选项2.3打包依赖项2.31导出依赖项列表2.32配置依赖项2.4自定义打包选项2.5打包完成后的文件2.6注意事项三、打包示例一、为什

uni-app tree(树状) 组件

先看效果:组件代码:<template><viewclass="next-tree"><viewclass="next-tree-mask":class="{'show':showTree}"@tap="_cancel"></view><viewclass="next-tree-cnt":class="{'show':

QT-day1

实现华清远见登陆界面#include"mywnd.h"#include<iostream>#include<QDebug>#include<QPushButton>#include<QLineEdit>#include<QLabel>MyWnd::MyWnd(QWidget*parent):QWidget(parent

C#+sqlserver+asp.net婚纱影楼管理系统

一、源码描述这是一款简洁十分美观的ASP.NET+sqlserver源码,界面十分美观,功能也比较全面,比较适合作为毕业设计、课程设计、使用,感兴趣的朋友可以下载看看哦二、功能介绍该源码功能十分的全面,具体介绍如下:婚纱影楼管理系统实现了以下功能1:套系管理,影楼的管理人员可以管理自己所提供的套系信息2:员工管理,影楼

【QT】day4

闹钟头文件:#ifndefWIDGET_H#defineWIDGET_H#include<QWidget>#include<QDebug>#include<QMouseEvent>//鼠标时间#include<QPaintEvent>//绘制时间#include<QPainter>//画家时间#include<QPix

优秀的golang开源框架

Web框架:Gin:高性能的Web框架,适用于构建RESTfulAPI和Web应用。Echo:快速和灵活的Web框架,适合构建微服务。Beego:全功能的Web框架,包括ORM、模板引擎等。Revel:全栈Web框架,提供了丰富的功能集。数据库ORM:GORM:强大的对象关系映射库,支持多种数据库。XORM:简单而强大

热文推荐