5.所有权

news2024/11/16 19:38:31

标题

  • 一、概念
  • 二、规则
  • 三、示例
    • 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[..]
}

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

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

相关文章

java float 丢失的小数位,科学计数法陷阱

从不同的table,不同的Sql 获取的统计数据,map中有float 也有double数据。 只要定义的数据类型是float,即使最后转成double,其实精度都会损失。 float通过科学计数法,10 N次方,对于大数没有办法保留小数位。…

Docker 基础使用 (4) 网络管理

文章目录 Docker 网络管理需求Docker 网络架构认识Docker 常见网络类型1. bridge 网络2. host 网络3. container 网络4. none 网络5. overlay 网络 Docker 网路基础指令Docker 网络管理实操 Docker 基础使用(0)基础认识 Docker 基础使用(1)使用流程概览 …

ON DUPLICATE KEY UPDATE 子句

ON DUPLICATE KEY UPDATE 是 MySQL 中的一个 SQL 语句中的子句,主要用于在执行 INSERT 操作时处理可能出现的重复键值冲突。当尝试插入的记录导致唯一索引或主键约束冲突时(即试图插入的记录的键值已经存在于表中),此子句会触发一…

如何免费获取云服务器

这几天刚入手了阿贝云的 “免费云服务器 ” ,接下来给大家讲讲如何免费注册阿贝云的免费云服务器 如何获取免费云服务器 打开阿贝云官网,注册并认证 即可以领取免费云服务器 阿贝云地址:https://www.abeiyun.com/ 服务器优势 永久免费&…

R语言:str_view函数和writeLines函数的区别

str_view和writeLines都是R语言中用于处理和查看字符串的函数,但它们有不同的功能和用途。 str_view str_view 是 stringr 包中的一个函数,用于直观地显示字符串中模式的匹配情况。它会在RStudio Viewer窗格中生成一个HTML小部件,突出显示字…

混剪素材哪里找?分享8个热门素材网站

今天我们来深入探讨如何获取高质量的混剪素材,为您的短视频和自媒体制作提供最佳资源。在这篇指南中,我将介绍几个热门的素材网站,让您轻松掌握素材获取的技巧,并根据百度SEO排名规则,优化关键词的使用,确保…

游戏提示emp.dll丢失是怎么回事,哪种解决方法更值得推荐

在玩电脑游戏时,我们经常会遇到一些错误提示,其中最常见的就是“找不到emp.dll,无法继续执行代码”。这个问题会导致游戏无法启动运行。那么,究竟是什么原因导致了这个问题的出现呢?本文将从多个方面进行详细解析&…

encoding Token和embedding 傻傻分不清楚?

encoding 编码 “encoding” 是一个在计算机科学和人工智能领域广泛使用的术语,它可以指代多种不同的过程和方法。核心就是编码:用某些数字来表示特定的信息。当然你或许会说字符集(Unicode)更理解这种概念,编码更强调这种动态的过程。而字符…

NVIDIA Jetson AI边缘计算盒子

这里写自定义目录标题 烧录系统安装Jetpack 烧录系统 选择一台Linux系统,或者VMware的电脑作为主机,烧录系统和后面安装Jetpack都会用到。 根据供应商的指令烧录的,暂时还没验证官方烧录(后续验证补充)。 安装Jetpac…

tmux 移植到ARM板端运行环境搭建

tmux源码下载: Home tmux/tmux Wiki GitHub 依赖的库代码下载: libevent: ncurses: 第一步:将以上三个代码解压放在同一个目录下,逐个编译 1. cd ./libevent-2.1.12-stable ./configure --host"arm-nextvp…

rockchip ARM TrustZone

系统架构 分为安全世界和非安全世界。Loader、Trust 运行在安全世界;U-Boot、kernel、Android 运行在非安全世界里(安全的 driver、APP 除外)。 “hypervisor”常见释义为“管理程序”“虚拟机监控器”“超级监督者”。 它是一种可以创建、…

63-目录操作(QDir类)及展示系统文件实战

一、目录操作(QDir 类) #include <QCoreApplication>#include <QDir> #include <QStringList> #include <QtDebug>// 自定义函数实现获取目录下大小qint64 GetDirFileInfoSizeFunc(const QString &qpath) {// QDir类专门用来操作路径名称或底层文…

springboot三层架构与MVC,以及三层架构入门

三层架构与MVC 1. 三层架构是什么 把各个功能模块划分为表示层&#xff0c;业务逻辑层&#xff0c;和数据访问层三层架构&#xff0c;各层之间采用接口相互访问&#xff0c;并通过对象模型的实体类&#xff08;model&#xff09;作为数据传递的载体&#xff0c;不同的对象模型…

任务倒计时App

设计背景 在某一阶段可能需要给自己设置长期任务&#xff0c;比如找工作、考研等&#xff0c;需要一个单纯的任务计时工具&#xff0c;设置完任务的目标时间后&#xff0c;每次打开App时都能直接看到最新的剩余时间 设计步骤 1. 写java源码 由于需要界面显示&#xff0c;需…

pycharm下载和配置Anaconda环境

文章目录 一、 pycharm安装二、 安装 一、 pycharm安装 官网下载&#xff1a; https://www.jetbrains.com/pycharm/download/#sectionwindows Pycharm community Edition是免费的 二、 安装 安装目录有需要的可以自己更改。 三、创建项目 打开pycharm->继续->不发送。…

【UE数字孪生学习笔记】 虚幻日志系统

声明&#xff1a;部分内容来自于b站&#xff0c;知乎&#xff0c;慕课&#xff0c;公开课等的课件&#xff0c;仅供学习使用。如有问题&#xff0c;请联系删除。 部分内容来自UE官方文档&#xff0c;博客等 虚幻日志系统 1. 日志是一种非常实用的调试工具&#xff0c;可以详细…

AI赋能银行国际结算审单:合合信息抽取技术的实践与应用

官.网地址&#xff1a;合合TextIn - 合合信息旗下OCR云服务产品 时下&#xff0c;银行国际业务是金融体系的重要组成部分&#xff0c;涵盖了外汇交易、国际结算、贸易融资、跨境投资等领域&#xff0c;这些业务对于国际贸易和全球经济发展具有重要作用。国际业务部门单据、凭证…

renren-fast-vue在mac上的运行

被这个折磨好久了&#xff0c;终于成功了。。 版本号-node-14 需要提前执行的命令&#xff0c;希望可以帮助到大家。分别是解决版本在mac m1架构上的不兼容问题&#xff0c;另外解决没有验证码的问题&#xff0c;要注意数据库的配置&#xff0c;账号密码是否正确。 npm inst…

C语言中指针的说明

什么是指针&#xff1f; 在C语言当中&#xff0c;我们可以将指针理解为内存当中存储的地址&#xff0c;就像生活当中&#xff0c;一个小区里面&#xff0c;在小区里面有很单元&#xff0c;每一栋单元&#xff0c;单元内的房间有着不同的房间号&#xff0c;我们可以同过几栋几单…