5.所有权

标题

  • 一、概念
  • 二、规则
  • 三、示例
    • 3.1 变量作用域
    • 3.2 所有权的移交(深拷贝与浅拷贝)
    • 3.3 函数与所有权
    • 3.4 返回值与作用域
    • 3.5 引用的使用
  • 四、切片(&str)

一、概念

  • 所有权是Rust的核心特性。
  • 所有程序在运行时都必须管理它们使用计算机内存的方式。Rust的内存是通过一个所有权系统来管理的,其中包含一组编译器在编译时检查的规则。
  • 在Rust中,一个值是在栈还是堆上对语言的行为和为什么要做某些决定是有更大的影响的。
  • Rust通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。在运行时,所有权系统的任何功能都不会减慢程序。

二、规则

  • Rust 中的每一个值都有一个被称为其所有者(owner)的变量;
  • 值在任一时刻有且只有一个所有者
  • 当所有者(变量)离开作用域,这个值将被丢弃
  • 函数参数的传递也会造成所有权的转移;
  • 使用引用可以只使用变量而不转移所有权;
  • 一个引用的作用域从声明的地方开始一直持续到最后一次使用为止;

三、示例

3.1 变量作用域

  • 下面的变量x超出了{}的作用域范围,打印时报错cannot find value x in this scope
  • hello变量自动在堆内存中申请了空间,并且初始化为hello,等出了作用域(倒数第二个“}”号)后自动调用drop函数释放内存。
fn main() {
  {
    let x =  3;
  } 
  println!("x = {}", x);  //cannot find value `x` in this scope
   
  {
    let hello =  String::from("hello");
    println!("hello  = {}", hello );
  }
}

3.2 所有权的移交(深拷贝与浅拷贝)

  • 在堆上申请的内存,会在连续赋值的时候进行内存所有权的移交。
  • 可以使用clone函数进行堆内存的深拷贝
fn main() {
    let x = 5;
    let y = x;  //栈内存,没有任何影响

    let h = String::from("HelloWorld!");
    let l = h;   //已经进行了所有权的移交,h已不存在
    //let l = h.clone();  //可以使用clon()函数重新申请空间
    
    //println!("h = {}",h);  //error: value borrowed here after move(clone除外)
    println!("l = {}",l);
}

3.3 函数与所有权

将值传递给函数在语义上与给变量赋值相似。所有权转移的规则也相同

fn main() {
    let s = String::from("Hello");

    take_ownership(s);  //这里s发生了转移 

    let x = 5;

    makes_copy(x);     //栈上的变量x不受所有权影响

    println!("{} {}", x, s);   //s的所有权在take_ownership里,因此这里无法打印
}

fn take_ownership(src: String){
    println!("{}", src);
}

fn makes_copy(src: i32){
    println!("{}", src)
}

3.4 返回值与作用域

返回值可以把内存空间的所有权返回

fn main() {
    let s1 = gives_ownership(); //来源于gives_ownership中的some_string

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

    let s3 = takes_and_gives_back(s2);  //通过该函数所有权从s2转移到了s3

    println!("{}{}{}", s1, s2, s3);   //s2编译报错

}

fn gives_ownership() -> String {
    let src = String::from("hello"); 
    src   //返回src的所有权
}

fn takes_and_gives_back(a_string: String) -> String{
    a_string
}

3.5 引用的使用

在参数中使用引用就可以只传递变量而不传递所有权

fn main() {
    let mut s = String::from("Hello");

    alter_string_value(&mut s);  //只传递s的值而不转移所有权

    println!("{}", s);   //s依然有效,输出“Hello,world”
}

fn alter_string_value(src: &mut String){  //可变引用
    src.push_str(",world");
}

fn print_string_value(src: &String){
    src.push_str(",world");  //不可变引用,不能修改
}

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

fn main() {
    let mut s = String::from("hello");

    let r1 = &mut s;
    let r2 = &mut s;

    println!("{}, {}", r1, r2);
}

报错信息如下
在这里插入图片描述
这个报错说这段代码是无效的,我们不能在同一个作用域内多次将 s 作为可变变量。第一个可变的引用在 r1 中,并且必须持续到在 println! 中使用它,但是在那个可变引用的创建和它的使用之间,我们又尝试在 r2 中创建另一个可变引用,它引用了与 r1 相同的数据。

这样做是为了避免数据竞争,数据竞争由三个行为造成:

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

禁止同时使用可变与不可变引用

fn main() {
    let mut s = String::from("hello");

    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    let r3 = &mut s; // 大问题

    println!("{}, {}, and {}", r1, r2, r3);
}

改成下面这样就行了,依然是作用域的问题。

fn main() {
    let mut s = String::from("hello");

    let r1 = &s; // 没问题
    let r2 = &s; // 没问题

    println!("{} {}", r1, r2);
    let r3 = &mut s; // 没问题

    println!("{}", r3);
}

四、切片(&str)

  • 切片(slice)允许引用集合中一段连续的元素序列,而不用引用整个集合;
  • 字符串字面量就是切片,因此它是不可变的;
  • 可以采用字符串切片&str作为参数类型,因此这样就可以同时接收String和&str类型的参数了;
  • 定义函数时使用字符串切片代替字符串引用会使我们的API更加通用,且不会损失任何功能;

切片示例

fn main() {
    let s = String::from("hello world!");
    let hello = &s[0..5];  //hello,取0到4字符, 也可以写成&s[..5]
    let world = &s[5..];   //world,取6到最后

    let whole = &s[..];  //整个字符串
    // s.clear();
    println!("*{}*",hello);  //*hello*
    println!("*{}*",world);  //* world!*
    println!("{}", whole);   //hello world!
}

函数示例

fn main() {
    let s = String::from("hello world!");
    let wordIndex = first_world(&s[..]); //使用完整的切片
    println!("wordIndex = {}", wordIndex);

    let my_string_literal = "hello world";
    let wordIndex = first_world(&my_string_literal);

    println!("wordIndex = {}", wordIndex);

}

//获得第一个单词
fn first_world(s: &str) -> &str {
    let bytes = s.as_bytes(); //转换为字节序

    for (index, &item) in bytes.iter().enumerate(){
        if item == b' ' {
            return &s[..index];
        }
    }

    &s[..]
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ComfyUI 快速搭建流程

相关地址 ComfyUIPytorch版本 环境准备 nvidia 3090 ----------------------------------------------------------------------------- | NVIDIA-SMI 515.65.01 Driver Version: 515.65.01 CUDA Version: 11.7 | |--------------------------------------------…

从源码分析 vllm + Ray 的分布式推理流程

一、前言 随着 LLM 模型越来越大,单 GPU 已经无法加载一个模型。以 Qwen-14B-Chat 模型为例,模型权重大概 28GB,但是单个 NVIDIA A10 仅有 24GB 显存。如果想要在 A10 上部署 Qwen-14B-Chat 模型,我们需要将模型切分后部署到 2 个…

三十二、 数据跨境传输场景下的 PIA 与数据出境风险自评估是一回事吗?

PIA 与数据出境风险自评估并不相同。PIA 是《个人信息保护法》第五十五条明确提出要求企业在向境外提供个人信息前应当开展的自评估工作,而数据出境风险自评估则是《评估办法》第五条提出的要求符合数据出境安全评估申报情形的企业在申报前应当开展的自评估工作。 换…

阿里云活动推荐:AI 应用 DevOps 新体验

活动简介 阿里云新活动,体验阿里云的云效应用交付平台。体验了下,总体感觉还不错。平台把常规的开发过程封装成了模板,部署发布基本都是一键式操作,并且对自定义支持的比较好。 如果考虑将发布和部署搬到云上,可以玩一…

后端项目实战--瑞吉外卖项目软件说明书

瑞吉外卖项目软件说明书 一、项目概述 瑞吉外卖项目是一个外卖服务平台,用户可以通过该平台浏览餐厅菜单、下单、支付以及追踪订单状态。产品原型就是一款产品成型之前的一个简单的框架,就是将页面的排版布局展现出来,使产品得初步构思有一…

高效处理风电时序数据,明阳集团的 TDengine 3.0 应用实录

作为全国 500 强企业,明阳集团在风电行业拥有领先实力。目前全球超过 800 个项目采用明阳各种型号风电机组,安装数量超过 15000 台。每台风电机组配备数百至上千个监测点,生成的时序数据每秒一条,每天产生亿级以上的数据量。这些数…

电商比价系统的搭建需要哪些方面着手准备?

搭建一个淘宝/京东比价系统所需的时间取决于多个因素,包括但不限于系统的复杂度、开发团队的规模与经验、数据源获取的难易程度、技术选型等。以下是一个大致的时间估计和考虑因素: 需求分析与设计: 确定系统的主要功能,如商品搜…

C# Web控件与数据感应之模板循环输出

目录 关于模板循环输出 准备数据源 ​范例运行环境 RepeatHtml 方法 设计与实现 如何获取模板内容 getOuterHtml 方法 getInnerHtml 方法 调用示例 小结 关于模板循环输出 数据感应也即数据捆绑,是一种动态的,Web控件与数据源之间的交互&…

Web学习_SQL注入_联合查询注入

UNION 操作符用于合并两个或多个 SELECT 语句的结果集, UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句 中的列名,并且UNION 内部的 SELECT 语句必须拥有相同数量的 列。 联合查询注入就是利用union操作符,将攻击者希望查询的语句…

VS2019+QT5.15调用动态库dll带有命名空间

VS2019QT5.15调用动态库dll带有命名空间 vs创建动态库 参考: QT调用vs2019生成的c动态库-CSDN博客 demo的dll头文件: // 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DLL3_EXPORTS // 符号…

CST软件眼图工具Eye Diagram Tools (中)--- Classical流程

距离上次眼图介绍快两年了,由于上期已经将重点推荐的方法(statistical流程)介绍了,所以一直没急着涉及这个话题。 仿真实例011:眼图工具Eye Diagram Tools(上) 先总结一下之前介绍过的内容&am…

Java对象的序列化与反序列化

序列化和反序列化是什么 当两个进程远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。比如:我们可以通过http协议发生字符串信息;我们也可以在网络上直接发生Java对象。发送方…

佐西卡在美国InfoComm 2024展会上亮相投影镜头系列

6月12日至14日,2024美国视听显示与系统集成展览会将在拉斯维加斯会议中心盛大开幕。这场北美最具影响力的视听技术盛会,将汇集全球顶尖的视听解决方案,展现专业视听电子系统集成、灯光音响等领域的最新技术动态。 在这场科技盛宴中&#xff0…

数据可视化后起之秀——pyecharts

题目一:绘制折线图,展示商家A与商家B各类饮品的销售额 题目描述: 编写程序。根据第9.3.1,绘制折线图,展示商家A与商家B各类饮品的销售额。 运行代码: #绘制折线图,展示商家A与商家B各类饮品的…

油气管道可视化:推动行业智慧化发展

图扑油气管道可视化系统集成多源数据,提供全景监控与预警功能,增强管道管理的安全性和响应速度,驱动行业智慧升级。

PS系统教程16

图案图章工具-印象派效果 另一种用法-印象派效果 新建图层选择图案进行绘画 如果绘画效果不强 进行画笔设置 选择复位所有锁定位置 将画笔改为硬边缘 为什么没有出来雨点效果 因为我们选择了印象派 新建图层取消勾选印象派 基本用法:可以去做图案和背景 使用…

充电宝哪个牌子最耐用最值得入手?多维度实测三款充电宝

在如今这个手机不离手的时代,充电宝已然成为我们生活中不可或缺的伙伴。但面对市场上琳琅满目的充电宝品牌和产品,我们常常陷入纠结:充电宝哪个牌子最耐用最值得入手呢?为了给大家提供最真实、最实用的参考,我们精心挑…

政安晨【零基础玩转各类开源AI项目】解析开源:gradio:在Python中构建机器学习Web应用

目录 下载项目 快速开始 Gradio能做什么? Hello, World Interface 类 组件属性 多输入和输出组件 一个图像示例 Blocks: 更加灵活且可控 你好, Blocks 更多复杂性 尝试 政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 收录专栏…

Stable Diffusion 改变光线的能力简直太强大了!

在没有 Stable Diffusion 的年代,对照片的光线进行后期处理,基本要依靠 Photoshop。比如添加一个曝光图层。这个技术对于形状简单的物体来说很方便,因为光线效果很好模拟。但对于形状复杂的主体,比如人来说,要想实现自…

革新内容创作:AI生成工具的力量

作为一名AI爱好者,我积累了许多实用的AI生成工具。今天,我想分享一些我经常使用的工具,这些工具不仅能帮助提升工作效率,还能激发创意思维。 我们都知道,随着技术的进步,AI生成工具已经变得越来越智能&…