Rust-所有权(ownership)

文章目录

  • 前言
  • 一、管理计算机内存的方式
      • 所有权规则
  • 二、Rust中的 move
      • Copy trait
  • 三、Rust中的 clone
  • 总结


前言

Rust入门学习系列-Rust 的核心功能(之一)是 所有权(ownership)。引入这个概念是为了更好的管理计算机的内存。下面篇幅让我们来研究下这个功能有什么神奇之处。


一、管理计算机内存的方式

常见的编程语言中计算机内存管理方式:

  • Java:Java使用Java虚拟机(JVM)来管理计算机内存。JVM有一个垃圾回收器(GC),用于自动回收不再使用的对象并释放内存。
  • .NET:.NET使用托管堆来管理内存。在.NET中,对象分配在托管堆上,当对象不再使用时,垃圾回收器会自动回收内存。
  • Go:Go使用垃圾回收器(GC)来管理内存。Go的GC使用标记-清除算法来识别和清除不再使用的对象,并回收相关的内存。
  • JavaScript:JavaScript使用自动垃圾回收器来管理内存。JavaScript引擎会周期性地检查对象是否不再被引用,如果对象没有引用,则会将其标记为垃圾并回收相关的内存。
  • Rust:Rust使用所有权系统来管理内存。在Rust中,每个值都有一个所有者,并且只有当前所有者可以访问和修改该值。当值的所有者超出作用域时,该值将被自动释放。

通过上面的几种常用语言的列举分析,所有运行的程序都必须管理其使用计算机内存的方式。一些语言中具有垃圾回收机制,在程序运行时不断地寻找不再使用的内存;在另一些语言中,开发者必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。在运行时,所有权系统的任何功能都不会减慢程序。

所有权规则

那么所有权管理有什么规则?所有权的规则:

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

二、Rust中的 move

在Rust中,move操作指的是将变量的所有权从一个变量转移到另一个变量。当对某个值进行move操作时,该值将不再有效,不能再被访问或使用。

move操作通常发生在以下几种情况下:

  1. 将一个变量赋值给另一个变量时,会发生move操作。例如:
let a = String::from("Hello");
let b = a; // move操作,a的所有权转移到b
// println!("{}", a); // 这里会出现编译错误,a已经不再有效
  1. 函数参数传递时,会发生move操作。例如:
fn take_ownership(s: String) {// ...
}let s = String::from("Hello");
take_ownership(s); // move操作,s的所有权转移到函数take_ownership中
// println!("{}", s); // 这里会出现编译错误,s已经不再有效
  1. 函数返回值时,会发生move操作。例如:
fn create_string() -> String {let s = String::from("Hello");s // move操作,将s作为返回值
}let s = create_string(); // move操作,create_string函数的返回值所有权转移到s
// println!("{}", s); // 这里会出现编译错误,s已经不再有效

需要注意的是,通过clone()方法可以创建值的深拷贝,而不是move操作。例如:

let a = String::from("Hello");
let b = a.clone(); // 深拷贝,a的所有权不会转移
println!("{}", a); // 正常打印 Hello
println!("{}", b); // 正常打印 Hello

除了移动操作,我们还可以借用值的引用来使用它,而不会转移所有权。借用可以是不可变的(引用为&T)或可变的(引用为&mut T)。以下是一个示例:

fn main() {let s1 = String::from("hello");let len = calculate_length(&s1); // 通过引用借用s1值的所有权,计算长度println!("The length of '{}' is {}.", s1, len);// s1.push_str(", world!"); // 这一行代码将导致编译错误,因为我们只借用了s1的不可变引用let mut s2 = String::from("hello");change(&mut s2); // 通过可变引用借用s2的值的所有权,进行修改println!("s2 = {}", s2);
}fn calculate_length(s: &String) -> usize {s.len()
}fn change(s: &mut String) {s.push_str(", world!");
}

在这个例子中,我们将s1的引用传递给calculate_length函数,它只借用了s1的值的不可变引用来计算其长度,而没有转移所有权。我们还可以将s2的可变引用传递给change函数,它通过可变引用修改了s2的值。

Rust通过引用和所有权规则实现了借用和移动操作。通过移动操作,我们可以转移值的所有权到新的变量中。通过借用操作,我们可以共享值的引用,而不转移所有权。这种机制在Rust中确保了内存安全和线程安全。

Copy trait

下面的代码可以运行~

fn main() {let x = 5;let y = x;println!("x = {}, y = {}", x, y);
}

Rust 有一个叫做 Copy trait 的特殊标注,可以用在类似整型这样的存储在栈上的类型上。如果一个类型实现了 Copy trait,那么一个旧的变量在将其赋值给其他变量后仍然可用。

那么哪些类型实现了 Copy trait 呢?你可以查看给定类型的文档来确认,不过作为一个通用的规则,任何一组简单标量值的组合都可以实现 Copy,任何不需要分配内存或某种形式资源的类型都可以实现 Copy 。如下是一些 Copy 的类型:

所有整数类型,比如 u32。
布尔类型,bool,它的值是 true 和 false。
所有浮点数类型,比如 f64。
字符类型,char。
元组,当且仅当其包含的类型也都实现 Copy 的时候。比如,(i32, i32) 实现了 Copy,但 (i32, String) 就没有。

三、Rust中的 clone

Rust中的clone是一种方法,用于创建一个数据的完全副本。它会创建一个与原始数据相同的新数据,但是新数据独立于原始数据,具有自己的所有权。

在Rust中,数据的所有权规则决定了每个值在任何给定时间点只能有一个所有者。当我们将一个值赋给另一个变量时,所有权会转移给新变量。当我们通过引用来传递值时,所有权不会转移,只是借用该值。

使用clone方法可以在不转移所有权的情况下创建数据的完整副本。这对于需要在多个地方使用同一份数据时非常有用,而不想转移所有权。

以下是一个简单的示例,演示了clone方法的使用:

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

在这个例子中,我们创建一个字符串s1,然后通过调用clone方法创建了一个s2的副本。两个变量现在都拥有自己的所有权,并且可以独立于彼此使用。

需要注意的是,clone方法是一个昂贵的操作,因为它会对数据进行深拷贝。在处理大型数据结构时,应该谨慎使用clone,以避免性能问题。

clone方法是Rust中用于创建完整副本的方法,而不转移数据所有权。它对于需要在不同地方使用相同数据副本的场景非常有用。


总结

Rust 是一种系统级编程语言,其最重要的特性是所有权系统。这个特性可以使程序员避免许多常见的内存安全问题。

在 Rust 中,每个值都有一个所有者。这个所有者负责分配和释放值的内存。当所有者离开作用域时,它会自动释放相应的内存。

Rust 的所有权系统通过三个规则来实现:

  1. 唯一性规则:每个值都只能有一个所有者。当值被分配给另一个所有者时,原来的所有者将失效。这可以有效防止两个所有者同时释放同一个内存。

  2. 移动语义:当将值赋给另一个变量时,它将从原来的变量中移动到新的变量中。这意味着原来的变量将失效,并且不能再使用它。这确保了所有权的唯一性规则。

  3. 借用规则:可以通过借用来暂时地使用一个值,而不改变所有权。借用是通过引用来实现的,借用的变量称为引用。引用只是对值的一个视图,它不具备所有权。借用规则规定了如何正确地使用和管理引用。

总结来说,Rust 的所有权系统通过唯一性规则、移动语义和借用规则保证了内存的安全性和有效性。这使得 Rust 可以在不使用垃圾回收机制的情况下,实现高性能和安全的系统级编程。

参考: Rust 中文文档

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

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

相关文章

【0260】pg_filenode.map文件分析(内含map文件读取、解析demo)

1. 关于pg内核map file map文件是关键数据:我们没有从丢失或损坏中恢复的自动方法。我们使用CRC来检测损坏。为了最小化更新失败的风险, map文件应该保持在不超过一个标准大小的磁盘扇区(即512字节(bytes)),并且我们使用就地覆盖而不是玩重命名游戏。 下面的结构布局被设…

【动态规划】【组合数学】1866. 恰有 K 根木棍可以看到的排列数目

作者推荐 【深度优先搜索】【树】【有向图】【推荐】685. 冗余连接 II 本文涉及知识点 动态规划汇总 LeetCode1866. 恰有 K 根木棍可以看到的排列数目 有 n 根长度互不相同的木棍,长度为从 1 到 n 的整数。请你将这些木棍排成一排,并满足从左侧 可以…

Yii2项目使用composer异常记录

问题描述 在yii2项目中,使用require命令安装依赖时,出现如下错误提示 该提示意思是:composer运行时,执行了yiisoft/yii2-composer目录下的插件,但是该插件使用的API版本是1.0,但是当前的cmposer版本提供的…

Rust语言之sha-256爆破

文章目录 一、实现Sha-256加密1.创建项目2.编写Cargo.toml文件3.编写程序代码 二、sha256爆破1.获取命令行参数2.读取文件3.校验输入参数4.暴力破解 一、实现Sha-256加密 SHA-256是一种安全哈希算法,主要特点是将输入的数据(无论长度)通过特定…

Jmeter的自动化测试实施方案(超详细)

🍅 视频学习:文末有免费的配套视频可观看 🍅 关注公众号:互联网杂货铺,回复1 ,免费获取软件测试全套资料,资料在手,涨薪更快 Jmeter是目前最流行的一种测试工具,基于此工…

如何在 Angular 中使用懒加载路由

简介 延迟加载 是一种限制加载用户当前需要的模块的方法。这可以提高应用程序的性能并减小初始捆绑包大小。 默认情况下,Angular 使用 急切加载 来加载模块。这意味着在应用程序运行之前必须加载所有模块。虽然这对许多用例可能是足够的,但在某些情况下…

Pdoc:生成优雅Python API文档的工具

Pdoc:生成优雅Python API文档的工具 在开发Python项目时,文档是至关重要的。它不仅提供了对代码功能和用法的了解,还为其他开发人员提供了参考和使用的便利。Pdoc是一个流行的文档生成工具,专为生成Python API文档而设计。本文将介…

【NextJS】整个项目跨域配置

项目跨域是指:本项目作为被访问方,由另一个项目对本项目发起fetch等动作获取数据页面数据 实验环境: next: 14.1.0react: ^18 配置文件:next.config.[mjs|js|ts] 假定原始范本内容: /** type {import(next).NextCon…

STM32 USART入门指南

对于刚开始涉足STM32微控制器编程的初学者来说,掌握其通用同步/异步接收/发送器(USART)功能是一项基本且必要的技能。USART在嵌入式系统中广泛用于串行通信。本指南旨在简明扼要地介绍USART的基础概念和基本步骤,并提供一个简单的…

扯淡的DevOps,我们开发根本不想做运维!

引言 最初考虑引用“ DevOps 已死,平台工程才是未来”作为标题,但这样的表达可能太过于绝对。最终,决定用了“扯淡的”这个词来描述 DevOps,但这并不是一种文明的表达方式。 文章旨在重新审视 DevOps 和平台工程,将分别…

【c语言】人生重开模拟器

前言: 人生重开模拟器是前段时间非常火的一个小游戏,接下来我们将一起学习使用c语言写一个简易版的人生重开模拟器。 网页版游戏: 人生重开模拟器 (ytecn.com) 1.实现一个简化版的人生重开模拟器 (1) 游戏开始的时…

php捕获Fatal error错误与异常处理

在php5的版本中&#xff0c;如果出现致命错误是无法被 try {} catch 捕获的&#xff0c;如下所示&#xff1a; <?phperror_reporting(E_ALL); ini_set(display_errors, on);try {hello(); } catch (\Exception $e) {echo $e->getMessage(); } 运行脚本&#xff0c;最终…

GO语言的变量与常量

1.变量 go是一个静态语言 变量必须先定义后使用变量必须要有类型 定义变量的方式&#xff1a; var 名称 类型 var 名称 值 名称 :值 例如&#xff1a; var num int 这样就存了一个num类型为int的变量 var num 1 上面使用简化的定义通过num自动判断后面的类型为int并…

什么台灯最好学生晚上用的?五大高口碑学生护眼台灯推荐

对于学生来说&#xff0c;晚上学习早已是家常便饭&#xff0c;其中如果光线不合适&#xff0c;很容易就会造成近视的情况。面对这样的商机&#xff0c;很多厂家纷纷涉足护眼台灯行业&#xff0c;无论技术成熟与否&#xff0c;都大打护眼卖点&#xff0c;其中难免含有大量水分。…

SpringMVC的执行流程

过去的开发中,视图阶段&#xff08;老旧JSP等&#xff09; 1.首先用户发送请求到前端控制器DispatcherServlet(这是一个调度中心) 2.前端控制器DispatcherServlet收到请求后调用处理器映射器HandlerMapping 3.处理器映射器HandlerMapping找到具体的处理器,可查找xml配置或注…

milvus insert api的数据结构源码分析

insert api的数据结构 一个完整的insert例子: import numpy as np from pymilvus import (connections,FieldSchema, CollectionSchema, DataType,Collection, )num_entities, dim 10, 3print("start connecting to Milvus") connections.connect("default&q…

网络原理 - HTTP/HTTPS(2)

HTTP请求 认识URL URL基本格式 平时我们俗称的"网址"其实就是说的URL(Uniform Resource Locator统一资源定位符). (还有一个唯一资源标识符,称为uri,严格来说,uri范围比url广). 互联网上的每一个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该…

HTB-Analytics

靶机的IP地址为10.10.11.233&#xff0c;攻击机的IP地址为10.10.16.30 信息收集 # nmap -sT --min-rate 10000 -p- 10.10.11.233 -oN port.nmap Starting Nmap 7.94 ( https://nmap.org ) at 2024-02-19 14:50 CST Warning: 10.10.11.233 giving up on port because retransm…

十字星K线(Doji)含义,fpmarkets澳福一分钟讲解

许多新手交易者遇到过这种奇怪的烛台&#xff0c;看起来就像一个十字架&#xff0c;没有主体上下有长长的影子&#xff0c;fpmarkets澳福肯定的告诉各位投资者&#xff0c;这种就是十字星K线(用Doji表示)&#xff0c;开盘价与收盘价一致&#xff0c;价格运动已经停止时出现在烛…

突发!某地区网络故障,格行随身WiFi成“救星”?现场直击!

近日&#xff0c;某地区突发网络故障&#xff0c;导致大量用户无法上网。然而&#xff0c;在这场网络危机中&#xff0c;一款名为“格行随身WiFi”的设备却意外走红&#xff0c;成为了当地的“网络救星”。究竟发生了什么&#xff1f;让我们一起来现场直击&#xff01; 据了解&…