【Rust】——所有权规则、内存分配

简介: 【Rust】——所有权规则、内存分配

🎯所有权规则

重要:

  1. Rust 中的每一个值都有一个 所有者owner)。
  2. 值在任一时刻有且只有一个所有者。
  3. 当所有者(变量)离开作用域,这个值将被丢弃。


🎯变量作用域

作用域scope)。作用域是一个项(item)在程序中有效的范围。

    {                      // s 在这里无效,它尚未声明
        let s = "hello";   // 从此处起,s 是有效的
 
        // 使用 s
    }                      // 此作用域已结束,s 不再有效

换句话说,这里有两个重要的时间点:

  • s 进入作用域 时,它就是有效的。
  • 这一直持续到它 离开作用域 为止。

目前为止,变量是否有效与作用域的关系跟其他编程语言是类似的。


🎯String类型

String比哪些基础的标量数据类型更复杂。

字符串字面值:程序里手写的哪些字符串值:

  • 他们是不可变的。
  • 非所有字符串的值都能在编写代码时就知道

这个类型管理被分配到堆上的数据,所以能够存储在编译时未知大小的文本。

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

这两个冒号 :: 是运算符,允许将特定的 from 函数置于 String 类型的命名空间(namespace)下,而不需要使用类似 string_from 这样的名字。

    let mut s = String::from("hello");
 
    s.push_str(", world!"); // push_str() 在字符串后追加字面值
 
    println!("{}", s); // 将打印 `hello, world!`


🎯内存和分配

       就字符串字面值来说,我们在编译时就知道其内容,所以文本被直接硬编码进最终的可执行文件中。这使得字符串字面值快速且高效。不过这些特性都只得益于字符串字面值的不可变性。

       对于 String 类型,为了支持一个可变,可增长的文本片段,需要在堆上分配一块在编译时未知大小的内存来存放内容。这意味着:

  • 必须在运行时向内存分配器(memory allocator)请求内存。
  • 需要一个当我们处理完 String 时将内存返回给分配器的方法。

       Rust 采取了一个不同的策略:内存在拥有它的变量离开作用域后就被自动释放。


       当变量离开作用域,Rust 为我们调用一个特殊的函数。这个函数叫做 drop,在这里 String 的作者可以放置释放内存的代码。Rust 在结尾的 } 处自动调用 drop。


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

在 Rust 中,多个变量可以采取不同的方式与同一数据进行交互。

    let x = 5;
    let y = x;

       这也正是事实上发生了的,因为整数是有已知固定大小的简单值,所以这两个 5 被放入了栈中。

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

       这看起来与上面的代码非常类似,所以我们可能会假设它们的运行方式也是类似的:也就是说,第二行可能会生成一个 s1 的拷贝并绑定到 s2 上。不过,事实上并不完全是这样。


       如下图所示:一个指向存放字符串内容内存的指针,一个长度,和一个容量。这一组数据存储在栈上。右侧则是堆上存放内容的内存部分。

ea74e50d0df0044f62350beec12bdcf5_0874454abf3447eab7dc5318cb907529.png

       我们将 s1 赋值给 s2String 的数据被复制了,这意味着我们从栈上拷贝了它的指针、长度和容量。我们并没有复制指针指向的堆上数据。

重点:之前我们提到过当变量离开作用域后,Rust 自动调用 drop 函数并清理变量的堆内存。不过图展示了两个数据指针指向了同一位置。这就有了一个问题:当 s2 和 s1 离开作用域,它们都会尝试释放相同的内存。这是一个叫做 二次释放(double free)的错误,也是之前提到过的内存安全性 bug 之一。两次释放(相同)内存会导致内存污染,它可能会导致潜在的安全漏洞。


       不过因为 Rust 同时使第一个变量无效了,这个操作被称为 移动move),而不是叫做浅拷贝。

       因为只有 s2 是有效的,当其离开作用域,它就释放自己的内存,完毕。


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

    let s1 = String::from("hello");
    let s2 = s1.clone();
 
    println!("s1 = {}, s2 = {}", s1, s2);

       原因是像整型这样的在编译时已知大小的类型被整个存储在栈上,所以拷贝其实际的值是快速的。这意味着没有理由在创建变量 y 后使 x 无效。换句话说,这里没有深浅拷贝的区别,所以这里调用 clone 并不会与通常的浅拷贝有什么不同。

       任何一组简单标量值的组合都可以实现 Copy,任何不需要分配内存或某种形式资源的类型都可以实现 Copy 。如下是一些 Copy 的类型:

  • 所有整数类型,比如 u32
  • 布尔类型,bool,它的值是 truefalse
  • 所有浮点数类型,比如 f64
  • 字符类型,char
  • 元组,当且仅当其包含的类型也都实现 Copy 的时候。比如,(i32, i32) 实现了 Copy,但 (i32, String) 就没有。
相关文章
|
1月前
|
存储 Rust 安全
Rust 中的动态内存分配
【10月更文挑战第10天】在Rust中,动态内存分配主要用于运行时按需分配内存,与静态分配不同,它能处理大小不确定的数据结构。Rust通过`Box`类型实现堆分配,`Vec`类型则用于动态数组,两者均内置智能内存管理。`Rc`和`Arc`提供引用计数机制,支持数据共享并确保内存安全。Rust的内存安全管理机制有效避免了悬空指针和双重释放等问题。
|
3月前
|
存储 Rust 安全
30天拿下Rust之所有权
在编程语言的世界中,Rust凭借其独特的所有权机制脱颖而出,为开发者提供了一种新颖而强大的工具来防止内存错误。这一特性不仅确保了代码的安全性,还极大地提升了程序的性能。在Rust中,所有权是一种编译时检查机制,用于追踪哪些内存或资源何时可以被释放。每当一个变量被赋予一个值(比如:字符串、数组或文件句柄)时,Rust会确定这个变量是否“拥有”这个值,拥有资源的变量负责在适当的时候释放这些资源。
39 5
|
3月前
|
数据采集 Rust 安全
Rust在网络爬虫中的应用与实践:探索内存安全与并发处理的奥秘
【8月更文挑战第31天】网络爬虫是自动化程序,用于从互联网抓取数据。随着互联网的发展,构建高效、安全的爬虫成为热点。Rust语言凭借内存安全和高性能特点,在此领域展现出巨大潜力。本文探讨Rust如何通过所有权、借用及生命周期机制保障内存安全;利用`async/await`模型和`tokio`运行时处理并发请求;借助WebAssembly技术处理动态内容;并使用`reqwest`和`js-sys`库解析CSS和JavaScript,确保代码的安全性和可维护性。未来,Rust将在网络爬虫领域扮演更重要角色。
78 1
|
3月前
|
Rust 编译器 开发者
Cargo:Rust的神秘助手,它将如何改变包管理游戏规则?
【8月更文挑战第31天】Rust的包管理器Cargo简化了依赖管理和构建过程,与编译器无缝集成,提供从依赖下载到编译构建的全套解决方案。通过`cargo new`创建项目后,编辑`Cargo.toml`文件即可轻松管理依赖。Cargo还支持自动生成文档、运行测试及发布代码,并通过`crates.io`平台方便查找和分享Rust库,是Rust生态系统中的重要工具,有助于提升开发者生产力。
57 1
|
3月前
|
Rust 网络协议 安全
揭开Rust网络编程的神秘面纱:全新的Socket体验,让你告别内存泄漏的噩梦!
【8月更文挑战第31天】Rust语言凭借其卓越的内存安全性和高性能,在网络编程领域展现出独特优势。本文将带你探索Rust中的Socket编程,展示如何使用标准库`std::net`模块轻松实现TCP服务器与客户端。通过简洁的代码示例,你将看到Rust如何简化网络通信流程,并通过`async/await`异步模型高效处理并发连接。此外,Rust社区提供的优秀库如`tokio`和`async-std`进一步增强了异步网络编程的能力。无论是从基础示例还是高级应用,Rust都将为你带来耳目一新的网络编程体验。
277 0
|
3月前
|
Rust 安全 程序员
揭秘Rust语言的内存安全秘籍:如何构建坚不可摧的系统级应用?
【8月更文挑战第31天】Rust语言凭借其独特内存安全机制在编程领域脱颖而出,通过所有权、借用与生命周期等概念,在保证高性能的同时避免了缓冲区溢出等常见错误。本文深入探讨Rust的内存安全机制,并通过示例代码展示如何利用这些机制构建高效且可靠的系统。尽管这些机制增加了学习难度,但为软件开发奠定了坚实基础,使Rust成为系统、嵌入式及网络编程的理想选择。随着社区的发展,Rust将在未来软件开发中扮演更重要角色。
85 0
|
4月前
|
存储 Rust 安全
【Rust学习】04_所有权
所有权是 Rust 最独特的特性,对语言的其余部分有着深远的影响。它使 Rust 能够在不需要垃圾收集器的情况下保证内存安全,因此了解所有权的运作方式非常重要。在本章中,我们将讨论所有权以及几个相关功能:借用、切片以及 Rust 如何在内存中布局数据。
27 1
|
4月前
|
Rust Java C++
Rust 问题之内存泄漏如何解决
Rust 问题之内存泄漏如何解决
|
4月前
|
JavaScript Java 开发者
Rust 问题之在众多编程语言中关于内存管理有哪些分类
Rust 问题之在众多编程语言中关于内存管理有哪些分类
|
4月前
|
存储 Rust JavaScript
Rust 问题之TypeScript 代码,变量 s 存储在栈内存中还是堆内存中如何解决
Rust 问题之TypeScript 代码,变量 s 存储在栈内存中还是堆内存中如何解决