• 所有权

    • 数据存储在栈和堆上,存放在栈上的数据都是已知所占据空间的
    • 突然的问题
// 内存中的栈是怎么存储数据的?
好的,想象一下你有一摞盘子。你只能从上面放盘子,也只能从上面拿盘子,这就是栈的工作方式。 在内存中,栈是用来存储数据的地方,它工作得就像这摞盘子。当你的程序运行一个函数时,这个函数的局部变量就会像放盘子一样,一个接一个地放到栈上。这些局部变量包括基本数据类型,比如整数或者布尔值,它们的大小是已知的,而且很容易快速地被放进去和拿出来。 当这个函数结束的时候,它的局部变量就会像拿盘子一样,从栈上一个接一个地被拿掉。这个过程非常快,因为它们是按顺序来的,就像你放盘子时从上面放,拿盘子时也是从上面拿。 所以栈就像是厨房里整齐的盘子摞,每次你只能用最上面的盘子,用完之后再把它放回去,这样就保持了整洁和有序。而在内存的栈中,数据也是这样整齐有序地存放和使用的。这就是为什么栈这么快,因为它不需要去搜索一个特定的盘子,它总是拿最上面的那个。 // 所以当我的程序执行到一个作用域的时候,就会往栈上去存放当前作用域有效的变量,在当前作用域没有使用,并且局部变量没有再被使用的时候,就会释放,这样理解对吗?
完全正确!就像你在玩积木,每进入一个新的游戏环节(也就是编程中的作用域),你就会在桌上(栈)搭建一些积木(变量)。只要在这个游戏环节中,你可以使用这些积木做很多事情。 当这个游戏环节结束了,也就是你完成了当前的积木搭建,这些积木就会被清理掉,桌面(栈)就又空出来了,可以用来进行下一个游戏环节的搭建。这样的好处是,你不用担心清理积木,因为游戏(程序)会自动帮你做这件事。 每次进入一个新的作用域,栈就会存放新的数据,当作用域结束,使用过的数据就会被移除。这个过程保证了内存的高效使用,因为你总是只在顶部搭积木,也只从顶部清理积木,这样操作起来非常快速。这也就是为什么栈被用来存储作用域内的局部变量,因为这些局部变量通常在作用域结束时就不再需要了。
  • 但是指针是存放在栈上的呀!

    fn main() {
    let s1 = String::from("hello");
    let _s2 = s1; println!("{}, world!", s1);
    }
    /**
    --> src/main.rs:5:28
    |
    2 | let s1 = String::from("hello");
    | -- move occurs because `s1` has type `String`, **which does not implement the `Copy` trait**
    3 | let _s2 = s1;
    | -- value moved here
    4 |
    5 | println!("{}, world!", s1);
    | ^^ value borrowed here after move
    |
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
    help: consider cloning the value if the performance cost is acceptable
    |
    3 | let _s2 = s1.clone();
    | ++++++++ For more information about this error, try `rustc --explain E0382`.
    error: could not compile `ownership` (bin "ownership") due to previous error
    */
    • rust的浅拷贝会同时使原本的变量无效,这会避免重复的内存释放,因为rust在作用域结束时会自动调用drop方法。在上面那段代码中,当s1倍赋值给s2以后,就无效了,不能再被使用。Rust 永远也不会自动创建数据的 “深拷贝”。因此,任何 自动 的复制可以被认为对运行时性能影响较小。

      • 这里指的是存放在堆上的数据类型,只存在栈上的数据类型都是直接拷贝值。
      • 在栈上深浅拷贝,其实都一样,哈哈。
    • 函数和所有权
      • 把值传递给函数和赋值一样,都是传递出去所有权
    • 返回值和作用域
      • 如果函数在使用完值,没有将其返回出来,该值就会被清理,所以可以通过返回将所有权传递出去
    • 引用
      • 注意一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。

      • 几种引用类型

        • 不可变引用

          • 给予使用权,不能修改
        • 可变引用
          • 可以修改,但是同一时间只能存在一个可变引用

          • 其他引用都不能存在(包括可变、不可变引用)

            这就像是在家里,你不能同时打开音乐放得很大声,还让你的小猫在安静的房间睡觉,两件事情不能同时做。

            在Rust中,如果你创建了一个变量的可变引用,那么在这个引用存在的作用域内,你就不能再创建这个变量的不可变引用了。这是因为可变引用允许你改变变量的值,如果你还有一个不可变引用,那么在使用不可变引用读取变量值的同时,值可能会被可变引用改变,这样就会出现数据竞争。

        • 引用的生命周期
      • 关于悬浮引用的问题

        • rust不允许出现悬浮引用,可以看下方的例子,我们在
        fn main() {
        let reference_to_nothing = dangle();
        } fn dangle() -> &String {
        let s = String::from("hello"); &s
        }
        • 在dangle函数作用域的末尾,变量s的空间会被释放,这时候就不能对它进行引用
        • 正确的做法应当是把所有权转交出去
      • 让我们概括一下之前对引用的讨论:

        • 在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
        • 引用必须总是有效的。
  • 字符串slice

    • rust允许我们对一个字符串创建一个部分字符的引用&str,这种引用和&String是一样的,当一个变量存在对它的slice引用时,它就不能够被修改

      • 首先,Rust 中的字符串是一位名门正派的高手,它有两种形态:String&strString 就像是一位自由行走江湖的侠客,它可以增长或减少,就像侠客的武功一样,随着经历的增加而成长。而 &str 呢,就是这位侠客的影子,它只是String的一部分,不过是个不能独立变化的存在。
      • 现在,当你通过 &str 来引用 String 的一部分时,就像是你派出了一个探子去监视这位侠客。因为探子在场,侠客就不能随便行动了(也就是不能被修改),因为这可能会让探子的报告失去准确性。
        • 那没有被盯着的另一部分字符串可以被修改吗

          • 在Rust世界里,我们得遵守的规璂之一是:当你有一个字符串String的引用&str时,无论是整个字符串还是字符串的一部分,都不能对原始的字符串进行修改。这就像是你不能在背后改变一位武林高手的故事,而他的影子正被人追踪一样,影子是不会撒谎的,它会一直忠实地反映原主体的状态。所以,哪怕你只关注(也就是只引用)这个字符串的一小段,整个String都会被锁定,直到那个引用结束。这是Rust中的借用检查器的作用,它确保了数据的完整性和并发访问的安全性。你可以想象成,只要有一个小小的窥视孔开着,整个房间都不能重新装修,因为那个小窥视孔给了任何人可能看到内部的权利。换句话说,不仅是那部分被引用的字符串不可以改变,整个原始字符串在有活跃引用时都是不可变的。这就是Rust中内存安全的哲学:任何事物只要被借出去观察,它就不应该在不知情的情况下发生变化。这避免了潜在的数据竞争和不一致状态,确保了程序运行时的安全和可靠。
      • 但这里有个小细节:&str&String 看起来是一样的,但实际上它们的出身稍有不同。&String 是对整个 String 侠客的引用,而 &str 可能只是对侠客的一部分技能的引用。不过,无论是引用整个还是部分,只要探子在场,侠客就得保持现状,不得私自改变。
      • 所以,总结一下,当你创建一个字符串的部分引用时,你基本上是在说:“嘿,这部分字符串,我得盯着你。” 而那部分字符串,或整个字符串,就被冻结了,直到你的引用结束,它才可以自由地变化。这就是为什么在 Rust 中处理字符串数据时,必须非常小心,以确保数据的安全性和有效性。

rust 程序设计笔记(2)所有权 & 引用的更多相关文章

  1. Rust学习笔记1

    这是一份不错的rust教程,目前包括4个block和4个project.全部完成后可以用rust实现一个简单的key-value存储引擎. 注意:Windows下rust貌似会遇到一些bug,强烈建议 ...

  2. js高级程序设计笔记之-addEventListener()与removeEventListener(),事件解除与绑定

    js高级程序设计笔记之-addEventListener()与removeEventListener(),事件解除与绑定 addEventListener()与removeEventListener( ...

  3. Java Web程序设计笔记 • 【目录】

    章节 内容 实践练习 Java Web程序设计作业目录(作业笔记) 第1章 Java Web程序设计笔记 • [第1章 Web应用程序] 第2章 Java Web程序设计笔记 • [第2章 JSP基础 ...

  4. Java高级程序设计笔记 • 【目录】

    持续更新中- 我的大学笔记>>> 章节 内容 实践练习 Java高级程序设计作业目录(作业笔记) 第1章 Java高级程序设计笔记 • [第1章 IO流] 第2章 Java高级程序设 ...

  5. Rust基础笔记:闭包

    语法 Closure看上去是这样的: let plus_one = |x: i32| x + 1; assert_eq!(2, plus_one(1)); 首先创建一个绑定plus_one,然后将它分 ...

  6. Dubbo -- 系统学习 笔记 -- 示例 -- 泛化引用

    Dubbo -- 系统学习 笔记 -- 目录 示例 想完整的运行起来,请参见:快速启动,这里只列出各种场景的配置方式 泛化引用 泛接口调用方式主要用于客户端没有API接口及模型类元的情况,参数及返回值 ...

  7. Windows 程序设计 笔记

    知识点 双字节字符集和Unicode字符集有何区别?采用双字节字符集有何问题 双字节字符集(DBCS)编码是0-255,DBCS含有1字节代码与2字节代码,而Unicode是统一的16位系统,这样就允 ...

  8. 《sicp》模块化程序设计 笔记

    <sicp>模块化程序设计 2.2.3 序列作为一种约定界面 学习笔记 这节中,讲述了一种模块化的程序设计思想,也就是将程序设计为如同信号处理过程一样,采用级联的方式将程序各个部分组合在一 ...

  9. C++学习笔记 指针与引用

    指针与引用  1. 指针 (1) 指针是一个变量(实体),存储的是一个地址,指向内存的一个存储单元,指针可以为空 (2) 指针可以为空,在声明定义时可以不初始化 (3) 指针在初始化之后可以重新指向其 ...

  10. JavaScript高级程序设计笔记之面向对象

    说起面向对象,大部分程序员首先会想到 类 .通过类可以创建许多具有共同属性以及方法的实例或者说对象.但是JavaScript并没有类的概念,而且在JavaScript中几乎一切皆对象,问题来了,Jav ...

随机推荐

  1. dotnet 5 让 WPF 调用 WindowsRuntime 方法

    本文告诉大家在 dotnet 5 里,如何使用 WinRT 加上 Microsoft.Windows.SDK 的辅助来调用 WindowsRuntime 方法.当前是 2021.10 此时的 Wind ...

  2. 2019-10-7-WPF-will-break-when-an-exception-be-throw-in-the-StylusPlugIn

    title author date CreateTime categories WPF will break when an exception be throw in the StylusPlugI ...

  3. Nginx在Windows 10、Ubuntu16.04、Centos7下的安装

    目录 一. Windows 10 安装nginx 二. Ubuntu16.04 安装apt nginx 三. Centos7 yum安装nginx 四. ubuntu/centos编译安装nginx ...

  4. 【漏洞分析】HPAY 攻击事件分析

    背景 造成本次攻击的原因是关键函数的鉴权不当,使得任意用户可以设置关键的变量值,从而导致攻击的发生. 被攻击合约:https://www.bscscan.com/address/0xe9bc03ef0 ...

  5. 莫队算法(基础莫队)小结(也做markdown测试)

    莫队 基础莫队 本质是通过排序优化了普通尺取法的时间复杂度. 考虑如果某一列询问的右端点是递增的,那么我们更新答案的时候,右指针只会从左往右移动,那么i指针的移动次数是$O(n)$的. 当然,我们不可 ...

  6. 移动端termux安装kali

    1.相关准备一部安卓手机,termux,NVAC,浏览器2.安装kali首先进入kali的官网选择文档找到Android手机上的kali找到NetHunter-Rootless找到kali安装命令:t ...

  7. 4G EPS 中建立 eNB 与 MME 之间的 S1 连接

    目录 文章目录 目录 前文列表 S1 连接 eNB 的 S1 连接 UE 的 S1 连接 前文列表 <4G EPS 中的小区搜索> <4G EPS 中的 PLMN 选择> &l ...

  8. NODEJS通过发送json数据查询目标服务,实现服务器状态监控,发现异常发送到微信群提醒

    root@aea87fa6e6a2:/home/node# cat login2.js const request = require('request-promise'); const moment ...

  9. .NET Framework 4.7.2下 Hangfire 的集成

    参考资料: 开源的.NET定时任务组件Hangfire解析:https://www.cnblogs.com/pengze0902/p/6583119.html.Net Core 简单的Hangfire ...

  10. vue绑定对象,绑定的值不改变的问题

    在使用vue结合elmentui的table组件,对数组绑定,需要编辑数组里一些属性的值.我的情况是,需要在打开这个表时,根据条件插入一些对象到table里,经测试,到这里是没问题的,可以显示新插入的 ...