Rust开发——变量、静态变量与常量

1.变量

在 Rust 中,类型安全是通过静态类型系统来实现的。变量绑定默认情况下是不可变的(immutable)。

在 Rust 中声明一个变量时,默认情况下它是不可变的。例如:

fn main() {let x :i32 = 5; // 这是一个不可变绑定,默认情况下不可改变其值// x = 10; // 这将导致编译错误,因为 x 是不可变的
}

类型推断,i32是可选的

在上面例子中,x 被声明为一个不可变绑定,并初始化为 5。如果试图修改 x 的值(例如尝试将 x 的值改变为 10),将会导致编译错误,因为变量默认情况下是不可变的。

为了允许变量可变,可以使用 mut 关键字来显式地声明一个可变的绑定:

fn main() {let mut y = 5; // 使用 mut 关键字声明可变绑定y = 10; // 这是合法的,因为 y 是可变的println!("The value of y is: {}", y); // 输出 y 的值
}

使用了 mut 关键字来声明一个可变的绑定 y,然后成功将 y 的值修改为 10

2.类型推断

在 Rust 中,编译器会根据变量的使用方式来推断变量的类型。

fn main() {let x = 5; // 编译器根据赋值来推断 x 的类型为 i32let y = 3.14; // 编译器根据赋值来推断 y 的类型为 f64let sum = x + y; // 编译器根据 x 和 y 的使用来推断 sum 的类型println!("The sum is: {}", sum); // 输出 sum 的值
}

编译器根据赋值来推断 x 的类型为 i32y 的类型为 f64。然后,当 xy 作为操作数相加时,编译器会根据其使用来推断 sum 的类型。在这种情况下,sum 的类型会被推断为 f64,因为它是 i32f64 类型的混合运算结果。Rust 的类型推断功能允许编写更加简洁且灵活的代码,但也保持了类型安全。

值得注意的是,Rust 编译器可以根据变量声明和用法的约束来推断类型。像这样声明的变量并不是某种动态的“任意类型”,可以容纳任何数据。这种声明所生成的机器代码与显式声明类型的代码完全相同。只是编译器完成了这个转换。

下面的代码告诉编译器将某种泛型容器中的内容进行复制,而代码本身并没有显式地指定包含的类型,而是使用 _ 作为占位符:

fn main() {let mut container = Vec::new(); // Rust 根据使用推断出 container 的类型为 Vec<_>container.push(5); // 向向量中添加一个 i32 类型的元素container.push(3.14); // 向向量中添加一个 f64 类型的元素for element in container {println!("Element: {}", element);}
}

container 被初始化为一个空的向量 Vec<_>。下划线 _ 是一个占位符,告诉编译器根据插入向量的元素推断类型。在这种情况下,container 是一个可以容纳不同类型元素的向量(根据插入的元素类型推断出 i32f64)。

Rust 根据容器的使用推断出其类型,并允许其容纳不同类型的元素。但是,一旦推断出特定类型,容器的类型就固定了,除非另有规定,否则不会接受不同类型的元素。

3. 静态变量

静态变量在整个程序执行期间都会存在,并且不会发生移动(move)。

在 Rust 中,静态变量(static variables)拥有 'static 生命周期,它们在程序启动时被初始化,并且在整个程序的生命周期内保持不变。这些变量存储在程序的静态存储区域中,因此它们不会被销毁或移动。这意味着静态变量的生存周期与整个程序的生命周期相同。

由于静态变量的生命周期是 'static,因此其持续时间长达整个程序的运行期间,所以它们不会被移动。在 Rust 中,静态变量的引用可以安全地传递并在整个程序中使用,因为它们的生命周期不会结束或改变。

static BANNER: &str = "Welcome to RustOS 3.14";fn main() {println!("{BANNER}");
}

静态变量在使用时并不会被内联,并且具有实际关联的内存位置。这对于不安全编程和嵌入式代码非常有用,并且这些变量在整个程序执行期间都存在。当全局范围的值不需要对象标识时,通常更倾向于使用 const

由于静态变量可以从任何线程访问,因此它们必须是 Sync 的。可以通过 Mutexatomic 或类似的方式实现内部可变性(interior mutability)。也可以有可变的静态变量,但它们需要手动进行同步,因此对它们的任何访问都需要使用不安全的代码。我们将在关于不安全 Rust 的章节中探讨可变的静态变量。

4.常量(const)

常量在编译时被求值,它们的值会在使用的地方被内联。
在 Rust 中声明一个常量时,编译器会在编译期间计算常量的值,并且将该值内联到代码中使用该常量的地方。这意味着编译器会将常量的实际值直接替换到代码中,而不是在运行时进行计算。这种内联有助于提高代码的效率,并且保证了常量在运行时是不可变的。

例如:

const VALUE: i32 = 42;fn main() {let x = VALUE * 2; // 编译器会将 VALUE * 2 替换为 42 * 2,这个值会在编译期间被计算println!("The value of x is: {}", x);
}

VALUE * 2 的计算结果会在编译时被替换为 84,并且这个值会在运行时被直接使用。这种行为确保了常量在编译期间被求值,并且在编译生成的机器代码中直接被替换。

  • 常量(使用 const 声明)在使用时会被内联,其值会直接替换到代码中。
  • 标记为 const 的函数可以在编译时调用以生成常量值。尽管这些函数可以在运行时被调用,但其主要目的是在编译期间进行计算,生成常量值。

如果使用过C++编程度,在Rust 中的 conststatic时可以理解为:

  • const 在语义上类似于 C++ 的 constexpr。它代表着编译时常量,其值在编译期就已经确定,并且在运行时是不可变的。常量通常用于表示在程序运行期间不会变化的固定值。
  • 相比之下,static 更类似于 C++ 中的 const 或可变的全局变量。static 提供了对象标识:在内存中的地址以及对于具有内部可变性的类型(例如 Mutex<T>)所需的状态。它在整个程序执行期间存在,并且可以被多个线程访问。
  • 在大多数情况下,不太需要在运行时计算常量值,但有时它会比使用 static 更安全。使用 const 可以确保在编译时就确定了值,而 static 在一定程度上可能会引入运行时变化的可能性。
  • 可以通过 std::thread_local 宏创建 thread_local 数据。这种数据是每个线程独有的,每个线程都有自己的一份拷贝,因此线程间不会共享该数据。这对于在多线程环境下需要独立的数据拷贝时非常有用。

5.变量作用域与隐藏变量

在 Rust 中,可以使用作用域隐藏来重新定义同名变量,无论是来自外部作用域还是同一作用域的变量。

fn main() {let a = 5; // 创建变量 a{let a = "hello"; // 在新作用域中重新定义了同名变量 a,隐藏了外部作用域中的 aprintln!("Inner scope: {}", a); // 输出新作用域中的 a,值为 "hello"} // 这里的 a 超出了内部作用域的范围println!("Outer scope: {}", a); // 输出外部作用域中的 a,值为 5,因为内部作用域中的 a 不再可见
}

内部作用域中重新定义的 a 变量隐藏了外部作用域中的同名变量 a。在内部作用域中重新定义的 a 只在该作用域内可见,超出该作用域后,再次访问 a 时会得到外部作用域中的值。这种方式允许你在同一作用域中使用相同名称的变量进行重新定义,而不会产生冲突。

在 Rust 中,变量隐藏与变量的可变性是两种不同的概念。通过变量隐藏,重新定义的同名变量在同一作用域内会覆盖之前定义的变量,但它们的内存位置会同时存在,且都可以使用相同的名称,具体取决于代码中的位置。

变量隐藏允许重新定义具有相同名称的变量,并且新定义的变量可以拥有不同的类型。一开始,变量隐藏可能看起来有些晦涩,但它对于在 .unwrap() 之后保留值非常方便。

以下代码演示了为什么编译器不能简单地在重新定义一个作用域内的不可变变量时重用内存位置,即使类型没有发生变化。

fn main() {let x: i32 = 5;println!("Original x: {}", x);{let x: i32 = 10; // 在新的作用域内重新定义了同名变量 x,类型仍为 i32println!("Inner x: {}", x); // 输出内部作用域的 x,值为 10} // 这里的 x 超出了内部作用域的范围println!("Outer x: {}", x); // 输出外部作用域的 x,值为 5,因为内部作用域中的 x 不再可见let x: &str = "hello"; // 在同一作用域内,将 x 重新定义为字符串类型println!("New x: {}", x); // 输出重新定义的 x,值为 "hello"
}

在上面例子中,重新定义的 x 变量在内部作用域中隐藏了外部作用域中的同名变量 x,但它们的内存位置并不相同。即使类型没有发生变化,重新定义的 x 仍然会分配新的内存空间。这说明了变量隐藏与可变性不同,虽然它们都涉及变量值的改变,但是变量隐藏会重新分配内存,而不是简单地改变值。

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

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

相关文章

代码随想录算法训练营第三十一天| 455 分发饼干 376 摆动序列 53 最大子数组和

目录 455 分发饼干 376 摆动序列 53 最大子数组和 455 分发饼干 将胃口值与饼干进行排序使其从小到大。 从后向前遍历胃口值&#xff0c;并取得此时最大的饼干值&#xff0c;如果饼干大于当前胃口值则将答案res加一&#xff0c;并且将饼干减一。 class Solution {public in…

基于 Glibc 版本升级的 DolphinDB 数据查询性能优化实践

在高并发查询、查询需要涉及很多个分区的情况下&#xff0c;低版本的 glibc&#xff08;低于2.23&#xff09;会严重影响查询性能。需要升级 glibc 解决该问题优化性能。我们撰写了本文&#xff0c;通过 patchelf 工具修改可执行文件和动态库的 rpath&#xff0c;达到无需升级系…

专业课140+总分420+东南大学920专业综合考研,信息学院通信专业考研分享

专业课140总分420东南大学920专业综合考研&#xff0c;信息学院通信专业考研分享 我是三月开始系统考研备战&#xff0c;寒假先看的高数全书&#xff0c;奈何在家效率极其低下&#xff0c;才草草看了前三四章。回校后学习的比较认真&#xff0c;每天大概保持10个小时左右&…

3分钟看完NVIDIA GPU架构及演进

近期随着 AI 市场的爆发式增长&#xff0c;作为 AI 背后技术的核心之一 GPU&#xff08;图形处理器&#xff09;的价格也水涨船高。GPU 在人工智能中发挥着巨大的重要&#xff0c;特别是在计算和数据处理方面。目前生产 GPU 主流厂商其实并不多&#xff0c;主要就是 NVIDIA、AM…

前端为什么不能直接连数据库

其实也不是不可以&#xff0c;只是这样做有很多不好的地方&#xff0c;但是如果是一个只有几个人用的内网小系统&#xff0c;是没有问题的。主要基于以下原因考虑 安全性问题&#xff1a;前端信息都是公开的&#xff0c;从前端访问数据库&#xff0c;就需要将数据库的地址&…

学习黑马AJAX

今天开始学习黑马的AJAX课程&#xff0c;一上来就是注册案例&#xff0c;开始很不习惯axios的语法&#xff0c;感觉好别扭&#xff0c;但前面花了比较长的时间重复敲&#xff0c;现在也熟悉了很多&#xff0c;后面以为的几节课都是围绕http协议的&#xff0c;首先是url的目标资…

利用OpenCV实现图片中导线的识别

下面是一个需求&#xff0c;识别图片中的导线&#xff0c;要在图像中检测导线&#xff0c;我们需要采用不同于直线检测的方法。由于OpenCV没有直接的曲线检测函数&#xff0c;如同它对直线提供的HoughLines或HoughLinesP&#xff0c;检测曲线通常需要更多的图像处理步骤和算法&…

java io流中为什么使用缓冲流就能加快文件读写速度

FileInputStream的read方法底层确实是通过调用JDK层面的read方法&#xff0c;并且这个JDK层面的read方法底层是使用C语言编写的&#xff0c;以实现高效的文件读取功能。但是它会涉及多次内核态与操作系统交互。当我们使用FileInputStream的read方法读取文件时&#xff0c;首先会…

UEC++ day7

敌人NPC机制 敌人机制分析与需求 新建一个character类来作为敌人&#xff0c;直接建蓝图设置骨骼网格&#xff0c;因为敌人可能多种就不规定死&#xff0c;然后这个敌人肯定需要两个触发器&#xff0c;一个用于大范围巡逻&#xff0c;一个用于是否达到主角近点进行攻击 注意我…

【Flink】Process Function

目录 1、ProcessFunction解析 1.1 抽象方法.processElement() 1.2 非抽象方法.onTimer() 2、Flink中8个不同的处理函数 2.1 ProcessFunction 2.2 KeyedProcessFunction 2.3 ProcessWindowFunction 2.4 ProcessAllWindowFunction 2.5 CoProcessFunction 2.6 ProcessJo…

https和http的区别和优势

大家好&#xff0c;我是咕噜-凯撒&#xff0c;HTTP&#xff08;超文本传输协议&#xff09;和HTTPS&#xff08;安全超文本传输协议&#xff09;是用于在网络上传输数据的协议&#xff0c;HTTPS相比HTTP在数据传输过程中更加安全可靠&#xff0c;适合对数据安全性要求较高的场景…

ventoy安装操作系统

下载ventoy https://github.com/ventoy/Ventoy/releases/download/v1.0.96/ventoy-1.0.96-windows.zip 解压后执行 Ventoy2Disk 2、安装后将ISO放入U盘大的分区&#xff0c;通过U盘启动就可以识别到ISO镜像开始装系统

MySQL 日志管理、备份与恢复

一、MySQL 日志管理 MySQL 的日志默认保存位置为 /usr/local/mysql/data vim /etc/my.cnf [mysqld] ##错误日志&#xff0c;用来记录当MySQL启动、停止或运行时发生的错误信息&#xff0c;默认已开启 log-error/usr/local/mysql/data/mysql_error.log #指定日志的保存位置…

PDF控件Spire.PDF for .NET【转换】演示:将 PDF 转换为 word、HTML、SVG、XPS

本文我们将演示如何通过调用 Spire.PDF 提供的方法 PdfDocument.SaveToStream() 将 PDF 页面转换为 HTML、Word、SVG、XPS、PDF 并将它们保存到流中。并且从Spire.PDF版本4.3开始&#xff0c;它新支持转换定义范围的PDF页面并将其保存到流中。 Spire.Doc 是一款专门对 Word 文…

springboot项目基于jdk17、分布式事务seata-server-1.7.1、分库分表shardingSphere5.2.1开发过程中出现的问题

由于项目需要&#xff0c;springboot项目需基于jdk17环境开发&#xff0c;结合nacos2.0.3、分布式事务seata-server-1.7.1、分库分表shardingSphere5.2.1等&#xff0c;项目启动过程中出现的问题解决方式小结。 问题一&#xff1a; Caused by: java.lang.RuntimeException: j…

职场Excel:求和家族,不简单

说到excel函数&#xff0c;很多人第一时间想到的就是求和函数sum。作为excel入门级函数&#xff0c;sum的确是小白级的&#xff0c;以至于很多人对求和函数有点“误解”&#xff0c;觉得求和函数太简单了。 但是&#xff0c;你可能不知道&#xff0c;sum只是excel求和家族里的一…

【打卡】牛客网:BM59 N皇后问题

自己写的&#xff1a; ①想自己定义结构体node&#xff0c;发现find函数太麻烦。看了眼模板&#xff0c;就用一个vector<int>记录行号就行&#xff0c;索引自然而然就是列号。 ②想用for循环写&#xff08;未通过&#xff09; 还在想这和模拟差不多。后来才意识到&…

Ubuntu22.04 交叉编译GCC13.2.0 for Rv1126

一、安装Ubuntu22.04 sudo apt install vim net-tools openssh-server 二、安装必要项 sudo apt update sudo apt upgrade sudo apt install build-essential gawk git texinfo bison flex 三、下载必备软件包 1.glibc https://ftp.gnu.org/gnu/glibc/glibc-2.38.tar.gz…

引迈-JNPF低代码项目技术栈介绍

从 2014 开始研发低代码前端渲染&#xff0c;到 2018 年开始研发后端低代码数据模型&#xff0c;发布了JNPF开发平台。 谨以此文针对 JNPF-JAVA-Cloud微服务 进行相关技术栈展示&#xff1a; 1. 项目前后端分离 前端采用Vue.js&#xff0c;这是一种流行的前端JavaScript框架&a…