当前位置: 首页 > news >正文

Rust 所有权

所有权

  • Rust的核心特性就是所有权
  • 所有程序在运行时都必须管理他们使用计算机内存的方式
    • 有些语言有垃圾收集机制,在程序运行时,他们会不断地寻找不再使用的内存
    • 在其他语言中,程序员必须显式的分配和释放内存
  • Rust采用了第三种方式:
    • 内存是通过一个所有权系统来管理的,其中包含一组编译器在编译时检查的规则
    • 在程序运行时,所有权特性不会减慢程序的运行速度。

Stack VS Heap

  • 在像Rust这样的系统级编程语言里,一个值是在还是在heap上对语言的行为和你为什么要做某些决定是有更大的影响的
  • 在你的代码运行的时时候,Stack和Heap都是你可用的内存,但他们的结构很不相同

存储数据

  • Stack按值的接收顺序来存储,按相反的顺序将他们移除(后进先出)
    • 添加数据叫压入栈
    • 移除数据叫弹出栈
  • 所有存储在stack上的数据必须拥有已知的固定的大小
    • 编译时大小位置的数据或运行时大小可能发生变化的数据必须存放在heap上
  • Heap内存组织性差一些
    • 当你把数据放入heap时,你会请求一定数量的空间
    • 操作系统在heap里找到一块足够大的空间,把它标记为在用,并返回一个指针,也就是这个空间的地址
    • 这个过程叫做在heap上进行分配,有时仅仅称为"分配"
  • 把值压到stack上不叫分配
  • 因为指针是已知固定大小的,可以把指针放在stack上
    • 但如果想要实际数据,你必须使用指针来定位
  • 把数据压到stack上比在heap上分配快得多
    • 因为操作系统不需要寻找用来存储新数据的空间,那个位置永远都是在stack的顶端
  • 在heap上分配空间需要做更多的工作
    • 操作系统首先需要找到一个足够大的空间来存放数据,然后要做号记录方便下次分配

访问数据

  • 访问heap中的数据要比访问stack中的数据慢,因为需要通过指针才能找到heap中的数据
    • 对于现代的处理器来说,由于缓存的缘故,如果指针在内存中跳转的次数越少,那么速度就越快
  • 如果数据存放的距离比较近,那么处理器的处理速度就会快一些(stack上)
  • 如果数据之间存放的距离比较远,那么处理速度就会慢一些(heap上)
    • 在heap上分配大量的空间也是需要时间的

函数调用

  • 当你的代码调用函数时,值被传入函数(也包括指向heap的指针)。函数本地的变量被压到stack上。当函数结束后,这些值会从stack上弹出

所有权存在的原因

  • 所有权存在的原因
    • 追踪代码的那些部分正在使用heap的哪些数据
    • 最小化heap上的重复数据量
    • 清理heap上未使用的数据以避免空间不足

所有权规则

  • 每个值都有一个变量,这个变量是该值的所有者
  • 每个值同时只能有一个所有者
  • 当所有者超出作用域(scope)时,该值将被删除

变量作用域

  • Scope就是程序中一个项目的有效范围
fn main(){// s不可用let s = "hello"; // s可用// 可以对s进行相关操作
}// s作用域到此结束,s不再可用

String类型

  • String比那些基础标量数据类型更复杂
  • 字符串字面值:程序里手写的那些字符串值。他们是不可变的
  • Rust还有第二种字符串类型:String。
    • 在heap上分配。能够存储在编译时未知数量的文本

创建String类型的值

  • 可以使用from函数从字符串字面值创建出String类型
fn main(){let mut s = String::from("Hello"); // ::表示from是String类型下的函数s.push_str(",world");println!("{}",s);
}
  • 这类字符串是可以被修改的
    • 因为他们处理内存的方式不同,

内存和分配

  • 字符串字面值,在编译时就知道他的内容了,其文本内容直接被硬编码到最终的可执行文件里
    • 速度快,高效。是因为其不可变性
  • String类型:为了支持可变性,需要在heap上分配内存来保存编译时未知的文本内容:
    • 操作系统必须在运行时来请求内存
      • 这步通过调用String::from来实现
    • 当用完String后,需要使用某种方式将内存返回给操作系统
      • 这步,在拥有GC的语言中,GC会跟踪并清理不再使用的内存
      • 没有GC就需要我们识别内存何时不再使用,并调用代码将他返回
        • 如果忘了,就会浪费内存
        • 如果提前做了,变量就会非法
        • 如果做了两次,也是Bug。必须一次分配对应一次释放
  • Rust采用了不同的方式:对于某个值来说,当拥有它的变量走出作用范围时,内存会立即自动的交换给操作系统。

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

  • 多个变量可以与同一个数据使用一种独特的方式来交互
fn main(){let x = 5;let y = x;// 整数是已知且固定大小的简单的值,这两个5被压到了stack中
}

String版本

fn main(){let s1 = String::from("hello");let s2 = s1;
}
  • 一个String由三部分组成
    • 一个指向存放字符串内容的内存的指针
    • 一个长度
    • 一个容量
  • 上面这些东西放在stack上
  • 存放字符串内容的部分在heap上
  • 长度len,就是存放字符串内容所需的字节数
  • 容量capacity是指String从操作系统总共获得的内存的总字节数

在这里插入图片描述

  • 当把s1赋给s2,String的数据被复制了一份
    • 在stack上复制了一份指针,长度,容量
    • 并没有复制指针所指向的heap上的数据
  • 当变量离开作用域时,Rust会自动调用drop函数,并将变量使用的heap内促释放
  • 当s1,s2离开作用域时,他们都会尝试释放相同的内存
    • 二次释放(double free)bug
  • 为了保证内存安全
    • Rust没有尝试复制被分配的内存
    • Rust让s1失效
      • 当s1离开作用域的时候,Rust不需要释放任何东西
fn main(){let s1 = String::from("hello");let s2 = s1;// 此时我们去使用s1println!("{}",s1);
}

会报错

在这里插入图片描述

  • 浅拷贝(shallow copy)
  • 深拷贝(deep copy)
  • 你也许会将复制指针,长度,容量视为浅拷贝,但由于Rust让s1失效了,所以我们用一个新的术语:移动(Move)
  • 隐含的一个设计原则:Rust不会自动创建数据的深拷贝
    • 就运行时性能而言,任何自动赋值的操作都是廉价的

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

  • 如果真想对heap上面的String数据进行深度拷贝,而不仅仅是stack上的数据,可以使用clone方法
fn main(){let s1 = String::from("hello");let s2 = s1.clone();println!!("{},{}",s1,s2);
}

在这里插入图片描述

Stack上的数据:复制

fn main(){let x = 5;let y = x;println!("{},{}",x,y);
}
  • Copy trait,可以用于像整数这样完全存放在stack上面的类型
  • 如果一个类型实现了Copy这个trait,那么旧的变量在赋值后仍然可用
  • 如果一个类型或者该类型的一部分实现了drop trait,那么Rust不允许让他再去实现Copy trait了

一些拥有Copy trait的类型

  • 任何简单标量的组合类型都可以是Copy的
  • 任何需要分配内存或某种资源的都不是Copy的
  • 一些拥有Copy trait的类型:
    • 所有的整数类型,例如u32
    • bool
    • char
    • 所有的浮点类型,例如f64
    • tuple(元组),如果其所有的字段都是Copy的

所有权与函数

  • 在语义上,将值传递给函数和把值赋给变量是类似的:

    • 将值传递给函数将发生移动复制
    fn main(){let s = String::from("Hello World");take_ownership(s); // s失效,因为函数结束后会调用drop方法let x = 5;makes_copy(x); // 因为分配在stack中,无事发生println!("x:{}",x)
    }fn take_ownership(some_string:String){println!("{}",some_string);
    }fn makes_copy(some_number:i32){println!("{}",some_number);
    }
    

返回值与作用域

  • 函数在返回值的过程中同样也会发生所有权的转移
fn main() {let s1 = gives_ownership();let s2 = String::from("hello");let s3 = takes_and_gives_back(s2); 
}fn gives_ownership() -> String {let some_string = String::from("hello");some_string
}fn takes_and_gives_back(a_string:String) -> String {a_string
}
  • 一个变量的所有权总是遵循同样的模式
    • 把一个值赋给其他变量时就会发生移动
    • 当一个包含了heap数据的变量离开作用域时,他的值就会被drop函数清理,除非数据的所有权移动到另一个变量上了

如何让函数使用某个值,但不获得其所有权

fn main(){let s1 = String::from("hello");let (s2,len) = calculate_length(s1);println!("the length of '{}' is {}",s2,len);
}fn calculate_length(s:String) -> (String,usize){let length = s.len();(s,length)
}
  • Rust有一个特性叫做"引用(Reference)"

引用和借用

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();
}
  • 参数的类型是&String而不是String
  • &符号就表示引用:允许你引用某些值而不取得其所有权
  • 我们把引用作为函数参数的这个行为叫做借用
  • 是否可以修改借用的东西?
    • 不行
  • 和变量一样,引用默认也是不可变的

可变引用

fn main(){let mut s1 = String::from("Hello");let len = calculate_length(&mut s1);println!("the length of '{}' is {}",s1,len);
}fn calculate_length(s:&mut String) -> usize {s.push_str(",world");s.len()
}
  • 可变引用有一个重要的限制:在特定作用域内,对某一块数据,只能有一个可变的引用
    • 这样做的好处是可在编译时防止数据竞争
  • 以下三种行为下会发生数据竞争:
    • 两个或多个指针同时访问同一个数据
    • 至少有一个指针用于写入数据
    • 没有使用任何机制来同步对数据的访问
  • 我们可以通过创建新的作用域,来允许非同时的创建多个可变引用
fn main(){let mut s = String::from("Hello");{let s1 = &mut s;}let s2 = &mut s;
}
  • 不可以同时拥有一个可变引用和不可变引用
  • 多个不可变的引用是可以的

悬空引用(Dangling References)

  • 悬空指针(Dangling Pointer):一个指针引用了内存中的某个地址,而这块内存可能已经释放并分配给其它人使用了
  • 在Rust里,编译器可保证引用永远都不是悬空引用。
    • 如果你引用了某些数据,编译器将保证在引用离开作用域之前数据都不会离开作用域

引用的规则

  • 在任何给定的时刻,只能满足下列条件之一
    • 一个可变的引用
    • 任意数量不可变的引用
  • 引用必须一直有效

切片

  • Rust的另外一种不持有所有权的数据类型:切片(Slice)
  • 字符串切片是指向字符串中一部分内容的引用
  • 形式:[开始索引…结束索引] (左闭右开)

注意

  • 字符串切片的索引范围必须发生在有效的UTF-8字符边界内
  • 如果尝试从一个多字节的字符中创建字符串切片,程序会报错并退出

将字符串切片作为参数传递

  • 采用&str作为参数类型,这样就可以同时接受String和&str类型的参数了
  • 定义函数时使用字符串切片来代替字符串引用会使我们的API更加通用,且不会损失任何功能。

相关文章:

Rust 所有权

所有权 Rust的核心特性就是所有权所有程序在运行时都必须管理他们使用计算机内存的方式 有些语言有垃圾收集机制,在程序运行时,他们会不断地寻找不再使用的内存在其他语言中,程序员必须显式的分配和释放内存 Rust采用了第三种方式&#xff1…...

Python面试题:结合Python技术,如何使用PyTorch进行动态计算图构建

PyTorch 是一个流行的深度学习框架,它通过动态计算图(Dynamic Computation Graphs)来支持自动微分(Autograd)。动态计算图的特点是每次前向传播时都会构建新的计算图,这使得它非常灵活,适合处理…...

基于RHEL7的服务器批量安装

目录 一、项目要求 二、实验环境 三、生成kickstart自动化安装脚本 四、搭建dhcp服务并测试kickstart脚本 五、搭建pxe网络安装环境实现服务器自动部署 ​编辑 六、测试 一、项目要求 1.使用kickstart编写自动化安装脚本 2.搭建dhcp服务并测试kickstart脚本 3.搭建px…...

C. Light Switches

文章目录 C. Light Switches题意:解题思路:解题代码: C. Light Switches 原题链接 题意: 房间的灯最初均为关闭状态,安装芯片后,它会每隔k分钟改变一次房间的灯光状态,即会打开灯光k分钟&…...

LabVIEW机器人神经网络运动控制系统

LabVIEW机器人神经网络运动控制系统 介绍了如何使用LabVIEW软件和中枢模式发生器(CPG)神经网络实现对舵机驱动爬壁机器人的精准运动控制。通过结合仿生控制理念与高级程序设计,本项目旨在开发一种能自动完成复杂墙面移动任务的机器人。 项目背景 现代机器人技术中…...

Qt WebEngine播放DRM音视频

Qt WebEngine播放DRM受保护视频,前提是Qt WebEngine开启音视频编码器,能够支持网页上普通视频的播放。开启音视频编码器需要自己编译源码,这里不做介绍。 什么是DRM音视频 DRM视频是指数字版权管理(Digital Rights Management&a…...

渗透小游戏,各个关卡的渗透实例

Less-1 首先,可以看见该界面,该关卡主要是SQL注入,由于对用户的输入没有做过滤,使查询语句进入到了数据库中,查询到了本不应该查询到的数据 首先,如果想要进入内部,就要绕过,首先是用…...

SpringBoot集成阿里百炼大模型(初始demo) 原子的学习日记Day01

文章目录 概要下一章SpringBoot集成阿里百炼大模型(多轮对话) 原子的学习日记Day02 整体架构流程技术名词解释集成步骤1,选择大模型以及获取自己的api-key(前面还有一步开通服务就没有展示啦!)2&#xff0c…...

高级java每日一道面试题-2024年8月06日-web篇-cookie,session,token有什么区别?

如果有遗漏,评论区告诉我进行补充 面试官: cookie,session,token有什么区别? 我回答: 在Web开发中,cookie、session和token是三种常见的用于用户身份验证和会话管理的技术。它们各自有不同的用途和优缺点,下面将详细解释: 1. Cookie 定…...

Python 图文:小白也能轻松生成精美 PDF 报告!

摘要: 还在为枯燥的数据报表发愁吗?想让你的 Python 项目报告瞬间高大上?本文将带你学习如何使用 Python 生成图文并茂的 PDF 文件,从此告别单调,让你的数据“活”起来! 一、 引言 想象一下,你正在为公司…...

AQS的ReentrantLock源码

什么是AQS(全称AbstractQueuedSynchronizer) 代表:重入锁、独占锁/共享锁、公平锁/非公平锁 是JUC包中线程阻塞、阻塞队列、唤醒、尝试获取锁的一个框架 AbstractQueuedSynchronizer是全称,是一个模板模式,一些线程…...

CSP-J 模拟题2

如果x大于45&#xff0c;则输出-1 设定一个整数now&#xff0c;他的初始值为9&#xff1b; 当x>now&#xff0c;就x-now&#xff0c;并且now--; 根据解析写代码1&#xff1a; #include <bits/stdc.h> using namespace std; int a[101010]; int main(){int x;cin>…...

途牛养车省养车平台源码 买卖新车租车二手车维修装潢共享O2O程序源码

源码采用FastAdmin框架开发&#xff0c;功能成熟完善&#xff0c;已有成功案例。 业务涵盖保险、二手车、接送、拖车、租车、保养、维修、入驻等连接线上等基础和深度服务。 采用的是“线上 车主直控社区加盟店” 模式&#xff0c;其主要考虑是布局门店有助于让目标消费用户…...

开发中遇到的gzuncompress,DomDocument等几个小问题以及一次Php上线碰到的502问题及php异常追踪

一、开发中遇到的gzuncompress,DomDocument等几个小问题记在此 1&#xff0c;昨天在命令行模式行运行一个很复杂的程序&#xff0c;一开始执行php&#xff0c;刚刚连接数据库&#xff0c;都没怎么查几条记录&#xff0c;&#xff08;publish:October 27, 2017 -Friday&#xff…...

【Material-UI】Button 组件中的基本按钮详解

文章目录 一、基本按钮变体1. 文本按钮&#xff08;Text Button&#xff09;2. 实心按钮&#xff08;Contained Button&#xff09;3. 轮廓按钮&#xff08;Outlined Button&#xff09; 二、应用场景与注意事项1. 使用场景2. 注意事项 三、总结 Material-UI 的 Button 组件是前…...

人工智能自动驾驶三维车道线检测—PersFormer模型代码详解

文章目录 1. 背景介绍2. 数据加载和预处理3. 模型结构4. Loss计算5. 总结和讨论 1. 背景介绍 梳理了PersFormer 3D Lane这篇论文对应的开源代码。 2. 数据加载和预处理 数据组织方式参考&#xff1a;自动驾驶三维车道线检测系列—OpenLane数据集介绍。 坐标系参考&#xff…...

LangChain +Streamlit+ Llama :将对话式人工智能引入您的本地设备成为可能(上篇)

&#x1f99c;️ LangChain Streamlit&#x1f525; Llama &#x1f999;&#xff1a;将对话式人工智能引入您的本地设备&#x1f92f; 将开源LLMs和LangChain集成以进行免费生成式问答&#xff08;不需要API密钥&#xff09; 在过去的几个月中&#xff0c;大型语言模型(LLMs)得…...

sql注入部分总结和复现

一个端口对应一个服务 联合查询注入 所有的程序中&#xff0c;单双引号必须成对出现 需要从这个引号里面逃出来 在后面查询内容 ?id1 要查库名&#xff0c;表名&#xff0c;列名。但是联合查询要知道有多少列&#xff0c;所以通过order by 去查询 order by # 通过二分法…...

开源企业级后台管理的快速启动引擎:Ballcat

Ballcat&#xff1a;快速搭建&#xff0c;高效管理&#xff0c;Ballcat让企业后台开发更简单。 - 精选真开源&#xff0c;释放新价值。 概览 Ballcat&#xff0c;一个专为企业级后台管理而设计的快速开发框架&#xff0c;以其高效的开发模式和全面的安全特性&#xff0c;为开发…...

FashionAI比赛-服饰属性标签识别比赛赛后总结(来自 Top14 Team)

关联比赛: FashionAI全球挑战赛—服饰属性标签识别 推荐大家看本篇博客之前&#xff0c;看一下数据集制作的方法&#xff0c;如何做一个实用的图像数据集 PS&#xff1a;我是参加完比赛之后才看的&#xff0c;看完之后&#xff0c;万马奔腾.....&#xff0c;因为发现比赛中还…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...