Rust所有权详解

文章目录

    • Rust所有权
      • 所有权规则
        • 作用域
      • 内存和分配
        • 移动与克隆
        • 栈空间
        • 堆空间
      • 关于函数的所有权机制
        • 作为参数
        • 作为返回值
      • 引用与租借
      • 垂悬引用

Rust所有权

C/C++中我们对于堆内存通常需要自己手动管理,手动申请和释放,即便有了智能指针,对于效率的影响和安全性问题也没有完全解决

Rust为了高效的使用和管理内存,以及对安全性的考量,提出了所有权的概念以及一系列规则

所有权规则

所有权有三条核心规则

  1. Rust中的每个值都有一个隐含的变量,称为所有者
  2. 一个值 同一时刻只能有一个所有者
  3. 当所有者离开作用域时,值会被丢弃(调用drop函数释放资源)
fn main() {let s1 = String::from("Hello"); // s1 是 "Hello" 的所有者let s2 = s1; // s1 的所有权被转移给 s2,s1 失效// println!("{}", s1); // ❌ 编译错误:s1 已经失去所有权println!("{}", s2); // ✅ s2 仍然是 "Hello" 的所有者
} // s2 离开作用域,"Hello" 被释放
作用域

这里的作用域和C/C++的作用域基本类似

{// 在声明以前,变量 s 无效let s = "runoob";// 这里是变量 s 的可用范围
}
// 变量范围已经结束,变量 s 无效

内存和分配

Rust是静态语言,这意味着我们无法像C++那样运行时扩容,例如vector会在满时进行扩容

在C++中=是赋值的意思,理解就是将一个变量的值,赋值给一个新的变量,这是一种拷贝语义(Copy)

但是在Rust中,变量和数据交互的方式主要是移动(Move)和克隆(Clone)

移动与克隆

如果你学过C++11,那你一定知道移动语义,例如移动拷贝或者移动赋值

这实际上是一种所有权的转移,主要是避免频繁申请和释放堆空间

但是有一些情况,我们并不希望只是所有权的转移,而是真的创建一个副本进行操作,这就需要使用clone()方法

fn main() {let s1 = String::from("hello");let s2 = s1.clone(); // 深拷贝println!("s1: {}, s2: {}", s1, s2); // ✅ 仍然可以使用 s1
}
栈空间

在栈空间内,Rust变量“移动”的方式其实就是复制,因为栈空间内基本上都是基本数据类型的,通常占用空间和复制时间不会很久,就会是直接复制,这时候两个变量都是可以使用的

    let x = 1;let y = x;println!("{x}, {y}");

"基本数据"类型有这些:

  • 所有整数类型,例如 i32 、 u32 、 i64 等。
  • 布尔类型 bool,值为 true 或 false 。
  • 所有浮点类型,f32 和 f64。
  • 字符类型 char。
  • 仅包含以上类型数据的元组(Tuples)。
堆空间

String对象的hello存储的位置可就不是栈空间了而是堆空间

例如

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

1.png

当执行到第二步时,s1就会把自己对hello字符串对所有权移交给s2,此时s1,就会失效

image.png

因此在移动之后,继续使用s1会报错

关于函数的所有权机制

作为参数

当一个变量作为参数传递给函数时,所有权应该怎么处理

fn main() {let s = String::from("hello");// s 被声明有效takes_ownership(s);// s 的值被当作参数传入函数,相当于s的所有权移交给函数了// 所以可以当作 s 已经被移动,从这里开始已经无效let x = 5;// x 被声明有效makes_copy(x);// x 的值被当作参数传入函数// 但 x 是基本类型,依然有效// 在这里依然可以使用 x 却不能使用 s} // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放fn takes_ownership(some_string: String) { // 一个 String 参数 some_string 传入,有效println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放fn makes_copy(some_integer: i32) { // 一个 i32 参数 some_integer 传入,有效println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放
作为返回值
fn main() {let s1 = gives_ownership();// gives_ownership 移动它的返回值到 s1let s2 = String::from("hello");// s2 被声明有效let s3 = takes_and_gives_back(s2);// s2 被当作参数移动, s3 获得返回值所有权
} // s3 无效被释放, s2 被移动, s1 无效被释放.fn gives_ownership() -> String {let some_string = String::from("hello");// some_string 被声明有效return some_string;// some_string 被当作返回值移动出函数
}fn takes_and_gives_back(a_string: String) -> String { // a_string 被声明有效a_string  // a_string 被当作返回值移出函数
}

引用与租借

这里的引用和C++中的引用是类似的,如果不了解C++的引用,也可以认为是一种指针

例如

fn main() {let s1 = String::from("hello");let s2 = &s1;println!("s1 is {}, s2 is {}", s1, s2);
}

按照原先的理解,s1内部会存一个指向"hello"的指针,s2内部其实也是一个指向"hello"的指针,但是s2是后来的,我们就认为s2是s1的一个引用,也就是别名

  • 引用可以认为是单独的一种类型
  • 引用不会获得值的所有权
  • 引用只能租借(Borrow)值的所有权
  • 当一个值被移动时,原先的引用会失效,必须重新租借所有权
fn main() {let s1 = String::from("hello");let mut s2 = &s1;let s3 = s1;s2 = &s3; // 重新从 s3 租借所有权println!("{}", s2);
}

一般的租借引用是不允许修改数据内容的,除非原先的数据是mut的,引用时也是mut引用,才允许修改

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

此时s2允许修改s1的内容

可变引用和不可变引用除了权限不同以外,可变引用不允许多重引用(多个变量引用同一个值),但是不可变引用是允许的

这样做主要是为了避免同时有多个使用者可以进行写操作

垂悬引用

这个其实就是对应着空指针的概念(已经释放资源的指针也算),例如

fn main() {let reference_to_nothing = dangle();
}fn dangle() -> &String {let s = String::from("hello");&s
}

s是在函数里申请的,函数结束自动释放,但是返回了s的引用,被main函数接收到了,这里就相当于得到了一个空指针

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/74817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【在线OJ项目测试报告】

朋友们、伙计们,我们又见面了,本期来给大家带来关于在线OJ项目的测试报告,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通 数…

【HFP】蓝牙HFP应用层核心技术研究

免提配置文件(Hands-Free Profile, HFP)作为实现设备间音频通信的关键协议,广泛应用于车载系统、蓝牙耳机等场景。本文将基于最新技术规范,深入剖析HFP应用层的功能要求、协议映射及编解码器支持,为蓝牙开发工程师提供详尽的技术指南。 一、HFP应用层功能全景图 HFP定义…

横扫SQL面试——PV、UV问题

📊 横扫SQL面试:UV/PV问题 🌟 什么是UV/PV? 在数据领域,UV(Unique Visitor,独立访客) 和 PV(Page View,页面访问量) 是最基础也最重要的指标&…

【C++】第八节—string类(上)——详解+代码示例

hello,又见面了! 云边有个稻草人-CSDN博客 C_云边有个稻草人的博客-CSDN博客——C专栏(质量分高达97!) 菜鸟进化中。。。 目录 一、为什么要学习string类? 1.1 C语言中的字符串 1.2 面试题(暂不做讲解) …

如何判断JVM中类和其他类是不是同一个类

如何判断JVM中的类是否为同一个类 在Java虚拟机(JVM)中,判断两个类是否相同需要同时满足以下三个条件: 1. 类全限定名必须相同 包括包名类名的完整路径必须完全一致例如:java.lang.String和com.example.String被视为不同类 2. 加载该类的…

ifconfig 使用详解

目录 一、基本语法二、常见用途及示例1. 查看所有网络接口信息2. 启用/禁用网络接口3. 配置 IP 地址和子网掩码4. 修改 MAC 地址5. 启用混杂模式(Promiscuous Mode)6. 设置 MTU(最大传输单元) 三、其他选项四、常见问题1. 新系统中…

1. 标准库的强依赖(核心原因)

1. 标准库的强依赖(核心原因) 容器操作(如 std::vector 扩容) 当标准库容器(如 std::vector)需要重新分配内存时,它会尝试移动现有元素到新内存,而非拷贝(为了性能&…

【MySQL】常用SQL--持续更新ing

一、配置信息类 1.查看版本 select version; 或 select version(); 2.查看配置 show global variables where variable_name in (basedir,binlog_format,datadir,expire_logs_days,innodb_buffer_pool_size,innodb_log_buffer_size,innodb_log_file_size,innodb_log_files_i…

Day82 | 灵神 | 快慢指针 重排链表

Day82 | 灵神 | 快慢指针 重排链表 143.重排链表 143. 重排链表 - 力扣(LeetCode) 思路: 笔者直接给跪了,这个难度真是mid吗 直接去看灵神的视频 环形链表II【基础算法精讲 07】_哔哩哔哩_bilibili 1.简单来说就是&#xf…

常见的微信个人号二次开发功能

一、常见开发功能 1. 好友管理 好友列表维护 添加/删除好友 修改好友信息(备注、标签等) 分组管理 创建/编辑/删除标签 好友分类与筛选 2. 消息管理 信息发送 支持多类型内容:文本、图片、视频、文件、小程序、名片、URL链接等 附加功…

Android打包及上架应用市场问题处理

一、Gradle 配置参数含义: compileSdkVersion: 29 表示项目编译时使用的 Android SDK 版本为 API 29(Android 10),仅影响编译阶段的行为(如代码语法检查、资源处理等),不直接影响运行时兼容性。…

Docker 从入门到进阶 (Win 环境) + Docker 常用命令

目录 引言 一、准备工作 1.1 系统要求 1.2 启用虚拟化 二、安装Docker 2.1 安装WSL 2 2.2 安装Docker Desktop 2.3检查是否安装成功 三、配置Docker 3.1 打开Docker配置中心 四、下载和管理Docker镜像 4.1 拉取镜像 4.2 查看已下载的镜像 4.3 运行容器 4.4 查看正…

计算机视觉5——运动估计和光流估计

一、运动估计 (一)运动场(Motion Field) 定义与物理意义 运动场是三维场景中物体或相机运动在二维图像平面上的投影,表现为图像中每个像素点的运动速度矢量。其本质是场景点三维运动(平移、旋转、缩放等&a…

介质访问控制——信道划分

什么是介质访问 介质访问(Medium Access)​ 是计算机网络中一种规则,用来解决 ​​“多台设备如何共享同一根网线/信道传输数据”​ 的问题。你可以理解为: 想象一条只能容一辆车通过的独木桥(网络中的网线、Wi-Fi信道…

ERP系统五大生产模式概述

制造业中,选择合适的生产模式是企业高效运营的关键。 以下是ERP系统支持的五大核心生产模式及其特点总结: 1. MTS(按库存生产) - 定义:先生产后销售,基于需求预测提前备货。 - 适用场景:需求稳定、标准化程度高的产品(如日用品、家电)。 - 优点:交货快、生产…

ubantu操作笔记

安装ssh服务 1.1 基本安装 sudo apt update sudo apt install openssh-server -y sudo systemctl start ssh sudo systemctl enable ssh 1.2 配置远程root登陆 # 0. 设置root密码 sudo passwd root # 1. 安装vim依赖 sudo apt-get install vim -y # 2. 编辑配置文件 s…

2-vim编辑器的安装和使用

一.常用工具介绍 前言: 我们想要编写c语言代码,可以使用linux系统提供的工具才能进行代码的编辑。代码编写后,我们还需要验证代码的书写正确。这就需要借助编译器来进行验证。linux系统为我们提供了比较好的开发工具。 vim编辑器&#xff…

小刚说C语言刷题——第16讲 switch语句

在日常生活中,我们经常会遇到多分支的情况。当分支较多时,我们可以用嵌套的if-else语句。但是这样会让结构显得混乱。这个时候我们可以考虑用switch语句。 1.语法格式 switch (表达式) { case 常量表达式1: 语句1; break; case 常量表达式…

使用 Python 连接 PostgreSQL 数据库,从 `mimic - III` 数据库中筛选数据并导出特定的数据图表

要使用 Python 连接 PostgreSQL 数据库,从 mimic - III 数据库中筛选数据并导出特定的数据图表,你可以按照以下步骤操作: 安装所需的库:psycopg2 用于连接 PostgreSQL 数据库,pandas 用于数据处理,matplot…

过孔的载流能力

PCB过孔的载流能力(即能安全承载的电流大小)主要与以下因素相关: 1. 过孔的尺寸 孔径(直径):孔径越大,横截面积越大,载流能力越强。 孔壁铜厚:电镀铜的厚度&#xff08…