1、变量声明语法

Rust 变量必须先声明,后使用。

对于局部变量,常见是声明语法为:

let variable : i32 = 100;

由于 Rust 是有自动推导类型功能的,所以后面的 :i32 是可以省略的。

1.1 语法解析更容易

局部变量声明一定是以 let 开头,类型一定是跟在冒号 : 的后面。语法歧义更少,语法分析器更容易编写。

1.2 方便引入类型推导功能

Rust 声明变量的特点:要声明的变量前置,类型描述后置。

这是因为在变量声明语句中,最重要的是变量本身,而类型其实是个附属的额外描述,并非必不可少的部分。如果我们可以通过上下文环境由编译器自动分析出这个变量的类型,那么这个类型描述完全可以省略不写。

PS:Rust 支持类型推导,在编译器能够推导类型的情况下,变量类型一般可以省略,但常量(const)和静态变量(static)必须声明类型。

Rust 从一开始就考虑了类型自动推导功能,因此类型后置的语法更加合适。

1.3 模式解构

let 表达式不仅仅用于变量的绑定,还能进行复杂变量的解构:从一个相对复杂的变量中,匹配出该变量的一部分内容:

fn main() {
let (a, mut b): (bool,bool) = (true, false);
// a = true,不可变; b = false,可变
println!("a = {:?}, b = {:?}", a, b); b = true;
assert_eq!(a, b);
}

2、变量命名规则

Rust 里的合法标识符(包括变量名、函数名、trait名等)必须由:

①、数字

②、字母

③、下划线

注意:不能以数字开头!!!

另外:要注意下划线 _ 的特殊用法。 如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,为了去掉这个警告,可以用下划线用作变量的开头。

fn main() {
let _x = 5;
let y = 10;
}

运行发现只有变量 y 提示警告了。

3、变量遮蔽

Rust 允许在同一个代码块中声明同样名字的变量,后面声明的变量会将前面声明的变量“遮蔽”起来。

//变量遮蔽
fn variable_masking(){
let x = "123";
println!("{}",x); let x = 1;
println!("{}",x);
}

注意:这样做并不会产生内存安全问题,因为我们对这块内存拥有完整的所有权,且此时并没有任何其它引用指向这个变量,对这个变量的修改是完全合法的。

4、变量类型推导

Rust的类型推导有两种:

①、从变量声明的当前语句中获取信息进行推导

②、通过上下文信息进行推导

//类型推导
fn type_derivation(){
//1.1 没有明确标出变量类型,但是通过字面量的后缀,编译器知道x的类型是 u8
let x = 5u8;
//1.2 通过字面量值 1,编译器知道y 的类型是 i32
let y = 1; //1.3 创建一个动态数组,但是没有声明数组里面是什么类型
let mut vec = Vec::new();
//编译器通过添加的元素 1 ,推导出 vec 的实际类型是 Vec<i32>
vec.push(1); println!("{}",x);
println!("{}",y);
println!("{:?}",vec); }

5、类型别名

通过 type 关键字给同一个类型起个别名。

//类型别名
fn type_alias(){
//将 i32 这种数据类型起别名为 int
type int = i32;
let x : int = 1;
println!("{}",x);
}

类型别名还可以用于泛型场景:

type Double = (T,Vec);

那么,以后使用 Double 的时候,就等同于(i32,Vec)

6、不可变 mut

Rust 声明的变量默认是不可变的。

默认不可变,这是一个很重要的特性,它符合最小权限原则,有助于我们写出正确且健壮的代码。

// 变量不可变
fn variable_mut(){
let i = 123;
i = 2;
}

编译报错:

如果要使得变量可变,必须要用关键字 mut 声明:

fn variable_mut(){
let mut i = 123;
i = 2;
}

当你使用 mut 却没有修改变量,Rust 编译期会友好地报警,提示你移除不必要的 mut。

fn main() {
variable_mut();
}
// 变量不可变
fn variable_mut(){
let mut i = 123;
println!("{}",i);
}

编译器警告:

7、静态变量

在 Rust 中,静态变量(Static Variables)是一种全局变量,其生命周期从程序开始直到程序结束,具有静态的存储期。

静态变量与普通的局部变量和堆上的动态分配变量有所不同。它们在编译时被初始化,并在程序的整个执行期间保持不变。

静态变量存储在只读的静态数据段中,可以在整个程序的作用域内共享和访问。

Rust 中通过 static 关键字声明静态变量,如下:

static NAME: Type = value;

比如声明一个静态变量 GLOBAL:

static GLOBAL : i32 = 0;

这也是 Rust 中唯一声明全局变量的方法。

由于 Rust 非常注重内存安全,因此全局变量的使用有很多限制:

①、全局变量必须在声明的时候马上初始化(对应局部变量可以先声明不初始化,只需要保证使用的时候初始化就行了,我们可以这样理解,全局变量是写在函数外面,而局部变量是写在函数内部,所以需要保证全局变量声明的时候就要初始化);

②、全局变量的初始化必须是编译期可确定的常量,不能包括执行期才能确定的表达式、语句和函数调用;

③、带有 mut 修饰的全局变量,在使用的时候必须使用 unsafe 关键字。

8、常量

Rust 中通过 const 关键字声明常量。如下:

const GLOBAL : i32 = 0;

①、使用 const 声明的是常量,而不是变量。因此不允许使用 mut 关键字修饰这个变量绑定,也不允许使用 let 关键字。

②、常量的初始化表达式也一定要是一个编译期确定的常量,不能是运行期的值。

③、常量名通常约定全部字母都使用大写,并使用下划线分隔单词。

④、常量声明一定要显式声明类型,不能省略。否则会编译报错。

const PI = 3.1415;  //  错误:missing type for `const` item

与静态变量相比,常量有以下几个区别:

  1. 不可变性:常量是不可变的,一旦初始化后就不能修改。而静态变量可以是可变的,但在 Rust 中通常也是不可变的。
  2. 编译时确定:常量的值在编译时就被确定,并且在程序运行时不能改变。静态变量的值在程序运行时也是不可变的,但它们的初始化可以包含运行时计算的值。
  3. 作用域和可见性:常量的作用域可以是全局的,也可以在特定的作用域内定义。它们可以通过使用 pub 关键字来进行公开,以便在其他模块中使用。静态变量具有全局可见性,可以在程序的任何位置访问和使用。
  4. 存储位置:常量通常直接嵌入到使用它们的代码中,并不占用额外的存储空间。而静态变量存储在只读的静态数据段中,可能占用额外的存储空间。

总结来说,常量是在编译时确定的不可变值,其值在运行时不可修改。它们在使用前就被初始化,并始终保持不变。常量具有诸如不可变性、编译时确定、作用域和存储位置等特性。静态变量与常量的区别在于可变性和初始化时机等方面。

9、变量声明常见错误

9.1 变量必须初始化才能使用

类型没有默认构造函数,变量值没有“默认值”

fn main() {
let x : i32;
println!("{}",x);
}

9.2 不能通过 mut 关键字修饰 const 声明的变量

这个很容易理解,常量是不可变的,mut 表示可变,放一起语义冲突了。

//mut和 const 不能一起使用
fn const_mut_error(){
const mut i : i32 = 1;
println!("{}",i);
}

9.3 使用下划线开头忽略未使用的变量

如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为这可能会是个 BUG。

但是有时创建一个不会被使用的变量是有用的,比如你刚刚开始创建一个项目。

如果你希望Rust 不要警告未使用的变量,可以用下划线作为变量名的开头:

fn main() {
let a = 5;
let _b = 6;
}

上面只有变量 a 警告了,变量 _b 没有警告。

10、为啥要手动设置变量可变性?

对比其它语言,比如 Java,默认变量是可变的,如果添加 final 关键字,表示变量在初始化后不可再被修改。

而 Rust 中的变量默认是不可变的。如果你想修改一个变量的值,需要在声明时使用 mut 关键字显式指定它为可变。

大概对比一下,从内存管理来看,Java是面向对象语言,内存管理依靠垃圾收集器,程序员不需要直接管理内存分配和释放。而Rust 则依赖所有权和生命周期系统提供了无垃圾收集的内存安全保证,编译器通过所有权规则来保证在任何给定时间,数据要么只有一个可变引用,要么有多个不可变引用,所以默认不可变是很有必要的。

另外,默认声明不可变,也会使得代码意图更明显,想要修改变量,必须增加额外的操作。

默认不可变,也可以防止数据被意外修改,rust 编译器也能够对不可变数据进行更多优化,提高程序的执行效率。

从并发上来讲,如果数据不会改变,就不需要加锁来同步访问,因此可以避免锁带来的开销和复杂性。这使得 Rust 在编写高效且安全的并发代码方面具有天然的优势。

Rust从入门到精通03-变量的更多相关文章

  1. Rust 从入门到精通01-简介

    1.rust 从哪里来 Rust语言在2006年作为 Mozilla 员工 Graydon Hoare 的私人项目出现,而 Mozilla 于 2009 年开始赞助这个项目.第一个有版本号的 Rust ...

  2. Rust 从入门到精通05-数据类型

    Rust 是 静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的类型. 在 Rust 中,每一个值都属于某一个 数据类型(data type),分为两大类: ①.标 ...

  3. Rust 从入门到精通06-语句和表达式

    1.语句和表达式 语句和表达式是 Rust 语言实现逻辑控制的基本单元. 在 Rust 程序里面,语句(Statement)是执行一些操作但不返回的指令,表达式(Expressions)计算并产生一个 ...

  4. Rust 从入门到精通03-helloworld

    安装完成 Rust 之后,我们可以编写 Rust 的 Hello Word.这里介绍两种方式,一种是rust原生方式,一种是利用 cargo 工具(重要) 1.rustc 方式 1.1 创建项目目录 ...

  5. 《Visual C# 从入门到精通》第一章使用变量、操作符和表达式——读书笔记

    前言: 这个笔记是我个人总结,主要是熟练自己查看<Visual C# 从入门到精通>(第8版)这本书时,懵然起总结的想法,只是总结一些知识点,在工作项目会用得上,但是对毫无C#语言基础的, ...

  6. Git版本控制软件结合GitHub从入门到精通常用命令学习手册(转)

    简要参考:http://www.tuicool.com/articles/mEvaq2 http://gitref.org/zh/index.html GIT 学习手册简介 本站为 Git 学习参考手 ...

  7. Nginx开发从入门到精通 学习目录分享学习 (阿里著作)

    Nginx开发从入门到精通   缘起 nginx由于出色的性能,在世界范围内受到了越来越多人的关注,在淘宝内部它更是被广泛的使用,众多的开发以及运维同学都迫切的想要了解nginx模块的开发以及它的内部 ...

  8. 【转】Git版本控制软件从入门到精通学习手册

    GIT 学习手册简介 本站为 Git 学习参考手册.目的是为学习与记忆 Git 使用中最重要.最普遍的命令提供快速翻阅. 这些命令以你可能需要的操作类型划分,并且将提供日常使用中需要的一些常用的命令以 ...

  9. CentOS7入门到精通实战课程课后习题

    Linux自动化运维系列①: CentOS7入门到精通实战--->传送门 http://edu.51cto.com/course/13055.html 01.系统入门课后习题 1.口述一个命令执 ...

  10. nginx教程从入门到精通

    [转]nginx教程从入门到精通 nginx教程写了一段时间,无意中发现,nginx相关文章已经达到了近100篇了.觉得很有必要汇总到一起,它是我们运维生存时间的一片心血,他是学习nginx的同学必看 ...

随机推荐

  1. PLSQL中&符号处理

    在SQL语句中的字符串中出现了&符号,当执行的时候会被认为是参数需要传递,如update product set brand = 'D&G'; 解决办法是把语句改为:update pr ...

  2. CF1930G Prefix Max Set Counting 题解

    题意: 给定一棵以 1 为根的有根树,求出其所有 dfs 序中前缀最大值序列的数量.\(n\le 10^6\). 思路 显然考虑 DP. 由于是求前缀最大值序列的方案数,因此如果一些点要出现在这个序列 ...

  3. 深入理解微服务架构:银弹 or 焦油坑?

    极客时间:<从 0 开始学架构>:深入理解微服务架构:银弹 or 焦油坑? 微服务与 SOA 的关系 SOA和微服务的关系和区别,可分为以下几种典型的观点: 微服务是 SOA 的实现方式 ...

  4. AI智能体策略FunctionCalling和ReAct有什么区别?

    Dify 内置了两种 Agent 策略:Function Calling 和 ReAct,但二者有什么区别呢?在使用时又该如何选择呢?接下来我们一起来看. 1.Function Calling Fun ...

  5. 2.3.net core 工作流WorkFlow流程(流程节点附件设置)

    流程节点附件设置 WikeFlow官网:http://www.wikesoft.com 有些流程要求某些节点必须上传附件. 你只需要在流程节点中配置附件的Key,附件名称,是否必传. 如下图: 文件存 ...

  6. Linux 统计活跃线程和线程数

    摘要:使用Linux命令ps -eT动态查看进程中,以指定字符串打头的活跃线程和线程数. 动态查看进程的线程数及活跃线程数 实现方案   在Linux系统中,可以使用以下命令来动态查看进程中名字包含& ...

  7. Linux命令之Telnet的使用方法

    无论是linux还是windows,在命令行下,telnet命令都可以用于查看某个远端主机端口或者服务域名是否可以访问,语法糖如下: telnet IP 端口 telnet 域名 端口(即:telne ...

  8. Spring Boot 整合ActiveMQ实现延时发现消息

    生产者提供两个发送消息的方法,一个是即时发送消息,一个是延时发送消息.延时发送消息需要手动修改activemq目录conf下的activemq.xml配置文件,开启延时.本文中maven依赖文件和ap ...

  9. 乒乓球测距(K210)

    测距 说明 识别的物体是乒乓球(规格应该是统一的吧), 硬件是K210,测距的函数经过拟合,在50cm范围内是准确的 如果使用起来不准确,可以打印代码中的LM, 然后去测数据,自己再拟合一个函数,代替 ...

  10. Spring扩展接口-内置事件ContextEvent

    .markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...