Rust 所有权(Ownership)

2023-10-28 15:20
文章标签 rust ownership 所有权

本文主要是介绍Rust 所有权(Ownership),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、Ownership是什么?
  • 二、规则集
  • 三、理论+示例
  • 四、引用和借用
  • 五、切片类型(slice)
  • 总结


前言

Rust的所有权概念是其最独特的特性之一,它对Rust的其它部分也有很大影响。所有权概念让Rust在没有GC的情况下,也能够保证内存安全(无需手动管理内存CC++)。

GC就是garbage collection,比较直观,JavaGo等均需要。
内存安全广义讲应该包括没有野指针、内存重复释放等错误。进一步的,在rust中结合其它特性可以实现多线程的并发安全访问,CC++常见的内存泄露则不属于不安全(虽然也是问题)。


一、Ownership是什么?

概括的讲,所有权是rust管理内存的一个规则集。
众所周知,计算机程序都需要管理内存,只是不同的编程语言采用了不同的方法。主要分两类:

  • 不断检测未使用内存并释放的GC(Java/go…)
  • 程序员手工管理内存(c/c++…)

而Rust采用第三种方法:通过一个包含规则集的所有权系统来管理内存。编译器会检查程序的内存使用是否符合规则集中的规则,违反规则的程序将无法编译通过。另外,这些规则的校验不会带来执行期的开销(编译期检查,编译很慢)。

规则有没有存在漏洞的可能性?导致内存不安全呢?目前看还没有

虽然对很多人来讲,所有权是一个新概念,需要时间来适应。但是,理解所有权是理解Rust独特性的基础。

二、规则集

所有权的规则集主要包括三条,可以类比C++中的RAIIunique_ptr等概念。

  • 每个值都有一个称之为owner的变量(有点类似C++ new完必须记得delete)
  • 任一时刻,每个value只有一个owner(即不可共享,实际多线程是可以共享的,不过需要用到unsafe等特性)
  • 程序执行出了owner作用域,对应的值会被丢弃(类似C++自动调用析构函数)

三、理论+示例

a. 简单情况

    {let s = String::from("hello"); // s is valid from this point forward// do stuff with s}                                  // this scope is now over, and s is no longer valid

以上为例,当s的作用域结束之时,即可以将s拥有的值所占用的堆(heap)空间释放。这个机制与C++的RAII( Resource Acquisition Is Initialization)类似,Rust通过调用drop,而C++调用析构函数。

b. 复杂情况

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

对于涉及到堆内存的对象,Rust默认采用浅复制。此外,s2 = s1执行之后,在rust中,s1将不再指向任何String对象,s2单独拥有字符串hello。图中的情况,其实更符合C++中的shared_ptr赋值后(引用计数增加)。
浅复制

解决了问题,和C++中的unique_ptr类似,shared_ptr的需求通过RcArc等特性解决。

深复制需要显式声明:

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

上述s1.clone()中的clone方法即是深拷贝,当然前提和C++一样,你得正确实现相应的函数,rust 中就是Clone trait。
深复制

Copy trait? 有什么意义吗?因为所有类型的对象都可以copy吧?

函数调用的实参,函数返回值,均会转移对象的所有权。

四、引用和借用

引用并没有对应值的所有权,因此不会触发调用drop。引用可以成为借用。

多个变量指向同一个对象,没有共享问题?单线程下不会,多线程下则不允许。

同一个对象不能同时被借用多次,下面代码编译错误。

fn main() {let mut s = String::from("hello");let r1 = &mut s;let r2 = &mut s;println!("{}, {}", r1, r2);
}

这个限制可以在编译期消除数据竞争。

但是,对象已经可以被两个变量修改了啊?可变引用变量+原来的变量,这两者的竞争如何解决呢,多线程不允许共享,如果需要共享,则需要用其它机制。

不可变引用可以有多个,但是可变引用只能有一个。当不可变引用完全无用之前,不能有新的可变引用。(所谓的共享不可变,可变不共享

编译期检测虚悬引用(dangling)
引用规则:

  • 任一时刻,只能由一个可变引用、或者若干不可变引用
  • 引用必须一直有效

五、切片类型(slice)

避免下面这种问题,s.clear()之后,s[word]访问其实已经非法,运行期报错。

thread ‘main’ panicked at ‘byte index 5 is out of bounds of ``’, src/main.rs:20:18

fn first_word(s: &String) -> usize {let bytes = s.as_bytes();for (i, &item) in bytes.iter().enumerate() {if item == b' ' {return i;}}s.len()
}fn main() {let mut s = String::from("hello world");let word = first_word(&s); // word will get the value 5s.clear(); // this empties the String, making it equal to ""// word still has the value 5 here, but there's no more string that// we could meaningfully use the value 5 with. word is now totally invalid!
}

总结

初看下来,Rust实现内存安全的机制,并没有什么高深的。其实就是可变不共享,共享不可变

这篇关于Rust 所有权(Ownership)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/294137

相关文章

【Rust练习】12.枚举

练习题来自:https://practice-zh.course.rs/compound-types/enum.html 1 // 修复错误enum Number {Zero,One,Two,}enum Number1 {Zero = 0,One,Two,}// C语言风格的枚举定义enum Number2 {Zero = 0.0,One = 1.0,Two = 2.0,}fn m

linux中使用rust语言在不同进程之间通信

第一种:使用mmap映射相同文件 fn main() {let pid = std::process::id();println!(

第二十四章 rust中的运算符重载

注意 本系列文章已升级、转移至我的自建站点中,本章原文为:rust中的运算符重载 目录 注意一、前言二、基本使用三、常用运算符四、通用约束 一、前言 C/C++中有运算符重载这一概念,它的目的是让即使含不相干的内容也能通过我们自定义的方法进行运算符操作运算。 比如字符串本身是不能相加的,但由于C++中的String重载了运算符+,所以我们就可以将两个字符串进行相加、但实际

【Rust光年纪】Rust 机器人学库全景:功能、安装与API概览

机器人学+Rust语言=无限可能:六款库带你开启创新之旅! 前言 随着机器人技术的快速发展,对于机器人学领域的高效、可靠的编程语言和库的需求也日益增加。本文将探讨一些用于 Rust 语言的机器人学库,以及它们的核心功能、使用场景、安装配置和 API 概览,旨在为机器人学爱好者和开发人员提供参考和指导。 欢迎订阅专栏:Rust光年纪 文章目录 机器人学+Rust语言=无限可能:

第二十二章 rust数据库使用:sea-orm详解

注意 本系列文章已升级、转移至我的自建站点中,本章原文为:rust数据库使用:sea-orm详解 目录 注意一、前言二、项目管理三、迁移文件四、实体文件五、业务使用 一、前言 只要开发稍微大型一点的项目,数据库都是离不开的。 rust目前并没有特别成熟的数据库框架,sea-orm这个框架是我目前所看到的成熟度最高的一个,并且仍在积极开发中。 所以本文将以sea-orm框

Rust使用之【宏】

一、简单使用clap clap = { version = "4.5.17", features = ["derive"] } 其中,什么是features = ["derive"]:表示你希望在添加 clap 依赖时启用 derive 特性。这通常意味着你希望使用 clap 的派生(derive)宏功能,这些功能可以简化创建命令行接口的代码。例如,derive 特性可以让你使用 #[der

第二十一章 rust与动静态库的结合使用

注意 本系列文章已升级、转移至我的自建站点中,本章原文为:rust与动静态库的结合使用 目录 注意一、前言二、库生成三、库使用四、总结 一、前言 rust中多了很多类型的库,比如前面章节中我们提到基本的bin与lib这两种crate类型库。 如果你在命令行执行下列语句: rustc --help 那么你将能找到这样的内容: --crate-type [bin|li

Rust的常数、作用域与所有权

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust到底值不值得学,之一  -CSDN博客 Rust到底值不值得学,之二-CSDN博客 Rust的数据类型-CSDN博客 3.7  常数的数据类型 在Rust语言中,变量有类型,常量也有类型。我们知道,在定义const常量的时候,就要

搭建Rust的开发环境

目标 我没有使用过Rust,但我听说它是一个可靠的语言,可以保证内存安全和线程安全。我对此很有兴趣,就想试一试这个语言。 在官网上有介绍他们所推荐的编辑器: 我将选择 Visual Studio Code (关于 【Visual Studio】和【Visual Studio Code】的区别:【VS】是完整的集成开发环境,而【VSCode】只算的上是一个相对轻量级的文本编辑器,并附带一些便捷

Rust的数据类型

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust到底值不值得学,之一  -CSDN博客 Rust到底值不值得学,之二-CSDN博客 3.5  数据类型的定义和分类 在Rust编程中,所谓数据类型,就是对数据存储的安排,包括存储单元的长度(占多少字节)以及数据的存储形式。不同的