Rust教程:How to Rust-变量

本文为第1篇

专栏简介

本专栏是优质Rust技术专栏,推荐精通一门技术栈的蟹友,不建议完全无计算机基础的同学

感谢Rust圣经开源社区的同学,为后来者提供了非常优秀的Rust学习资源

本文使用:

  • 操作系统macOS Sonoma 14 / Apple M1
  • 编译器:Rustc & Cargo

感谢一路相伴的朋友们,感谢你们的支持 ^ _ ^

Rust教程:How to Rust-变量

更新记录

2024.03.10 发布文章


前言

变量怎么命名?关键字有哪些?命名个和关键字名字一样的变量或者函数行不?变量绑定是啥?变量为啥还要可变?声明完不想用行不行?变量可以起一样的名字吗?


变量命名

在命名方面,Rust与其他编程语言并无显著区别,但当我们为变量、函数或类型等构造命名时,需要遵循一些既定的命名规则。这些规则有助于增强代码的可读性和一致性,具体规则可以看这里,一般来说,对于类型级别的构造(如结构体、枚举和特征),Rust倾向于使用驼峰命名法(CamelCase),即每个单词的首字母大写,且没有下划线分隔。而对于值级别的构造(如变量和函数参数),则推荐使用蛇形命名法(snake_case),即所有字母小写,单词之间用下划线分隔。

Rust语言包含一些保留关键字,这些关键字具有特殊的语法意义,因此不能被用作变量名或函数名。以下是一些常见的Rust关键字及其用途:

  • as - 强制类型转换,或useextern crate包和模块引入语句中的重命名
  • break - 立刻退出循环
  • const - 定义常量或原生常量指针(constant raw pointer)
  • continue - 继续进入下一次循环迭代
  • crate - 链接外部包
  • dyn - 动态分发特征对象
  • else - 作为 ifif let 控制流结构的 fallback
  • enum - 定义一个枚举类型
  • extern - 链接一个外部包,或者一个宏变量(该变量定义在另外一个包中)
  • false - 布尔值 false
  • fn - 定义一个函数或 函数指针类型 (function pointer type)
  • for - 遍历一个迭代器或实现一个 trait 或者指定一个更高级的生命周期
  • if - 基于条件表达式的结果来执行相应的分支
  • impl - 为结构体或者特征实现具体功能
  • in - for 循环语法的一部分
  • let - 绑定一个变量
  • loop - 无条件循环
  • match - 模式匹配
  • mod - 定义一个模块
  • move - 使闭包获取其所捕获项的所有权
  • mut - 在引用、裸指针或模式绑定中使用,表明变量是可变的
  • pub - 表示结构体字段、impl 块或模块的公共可见性
  • ref - 通过引用绑定
  • return - 从函数中返回
  • Self - 实现特征类型的类型别名
  • self - 表示方法本身或当前模块
  • static - 表示全局变量或在整个程序执行期间保持其生命周期
  • struct - 定义一个结构体
  • super - 表示当前模块的父模块
  • trait - 定义一个特征
  • true - 布尔值 true
  • type - 定义一个类型别名或关联类型
  • unsafe - 表示不安全的代码、函数、特征或实现
  • use - 在当前代码范围内(模块或者花括号对)引入外部的包、模块等
  • where - 表示一个约束类型的从句
  • while - 基于一个表达式的结果判断是否继续循环

以下关键字无任何功能,但仍然由 Rust 保留以备将来的应用

  • abstract
  • async
  • await
  • become
  • box
  • do
  • final
  • macro
  • override
  • priv
  • try
  • typeof
  • unsized
  • virtual
  • yield

原生标识符

原生标识符是Rust语言提供的一种机制,它允许开发者使用通常无法直接作为变量名、函数名或类型名等标识符的关键字。通过在关键字前加上r#前缀,开发者就可以"借用"这些关键字作为自定义的标识符,从而避免命名冲突和限制

例如,match是Rust中的一个关键字,用于实现模式匹配。如果我们尝试将match用作一个函数的名字,如下所示:

rust 复制代码
fn match(needle: &str, haystack: &str) -> bool
{
    haystack.contains(needle)
}

编译器提示:

rust 复制代码
error: expected identifier, found keyword `match`
 --> src/main.rs:4:4
  |
4 | fn match(needle: &str, haystack: &str) -> bool {
  |    ^^^^^ expected identifier, found keyword

编译器会直接报错,因为match是保留关键字,不能被用作普通函数名。此时,原生标识符就派上了用场。我们可以使用r#前缀来将match作为函数名称使用,如下所示:

rust 复制代码
fn r#match() -> i64
{
    let variable = 1;
    variable
}

fn main()
{
    let variable_output = r#match();
    println!("{}", variable_output)
}

通过这种方式,我们成功地规避了关键字带来的命名限制,实现了使用match作为函数名的目的。然而,需要注意的是,过度依赖原生标识符可能会导致代码可读性下降,因此在实践中应谨慎使用,并尽量使用更符合Rust命名习惯的替代方案


变量绑定

在JavaScript中,我们通常使用类似以下的方式为变量variable赋值:

ini 复制代码
var variable = "Hello World"

在上述代码中,计算机将等式右侧的字符串"hello world"赋值给变量variable。然而,在Rust中,我们采用了一种不同的方式来达到类似的效果:

ini 复制代码
let variable = "Hello World"

这个过程在Rust中被称为变量绑定,而不是赋值。那么,为什么Rust选择使用"绑定"这一术语而不是"赋值"呢?这背后涉及到Rust语言的核心原则------所有权。

简而言之,在Rust中,任何内存中的对象都有其特定的所有者,这个所有者完全掌控着该对象。当我们使用let关键字时,实际上是将一个内存对象绑定到一个变量上,这个变量随即成为该对象的所有者。与此同时,该对象之前的所有者(如果有的话)将失去对其的拥有权。这意味着在Rust中,一个对象在任何时刻只能有一个明确的拥有者。

这种所有权模型是Rust内存安全性的基石,它有助于防止诸如内存泄漏和悬挂指针等常见问题。通过明确绑定关系,Rust编译器能够跟踪哪些内存正在被使用,从而安全地进行清理和回收,确保程序的稳定运行。


可变变量

这个标题初听似乎有些出人意料,因为在大多数编程语言中,变量默认就是可变的。然而,在Rust中,情况恰恰相反。Rust中的变量默认是不可变的,包括我们之前提到的variable变量,其值一旦被绑定就不可再变。若需让变量可变,则需明确加上mut声明,类似下面这样

ini 复制代码
let mut variable = "Hello World"

这种语法可能初看起来有些多余,既然已经是变量了,为何还要额外声明它的可变性呢?

实际上,这种做法在大型项目中尤为重要。当一个变量被多处代码所引用时,有些代码可能期望该变量保持其原始值不变,而另一些代码则可能想要修改它。如果不加以明确声明,这种潜在的修改很难被及时发现,尤其是在多线程编程环境中,这种错误往往更为隐蔽和难以调试。因此,在Rust中通过mut关键字来明确变量的可变性,有助于提高代码的稳定性和可维护性。

这种规则让我们的代码逻辑变得非常清晰。当看到let关键字后面没有mut时,读者就能立即明白这个变量的值在后续代码中不会发生改变。而一旦看到mut,就像是给阅读代码的人打了"预防针",提醒他们这个变量的值在后续可能会被更改


下划线开头的变量

如果你创建了一个变量却不在任何地方使用它,Rust通常会给你一个警告,因为这可能会是个 bug。但是有时创建一个不会被使用的变量是有用的,你希望能保留它,那么就可以在变量开头加上下划线(在C或C++中,下划线开头的变量是不被允许的,因为这可能是编译器使用的变量,但在Rust中,这是可以的)例如

ini 复制代码
fn main()
{
    let _variable_a = 5;
    let variable_b = 10;
}

在编译它时,Rust会提示

vbnet 复制代码
warning: unused variable: `variable_b`
 --> variable.rs:4:9
  |
4 |     let variable_b = 10;
  |         ^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_variable_b`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: 1 warning emitted

可以看到,两个变量都是只有声明,没有使用,但是编译器却独独给出了variable_b未被使用的警告,充分说明了_变量名前缀在这里发挥的作用

值得注意的是,这里编译器还很善意的给出了提示( Rust的编译器非常强大,这里的提示只是小意思 ): 将variable_b修改成 _variable_b即可


变量遮蔽

Rust 允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的,例如

ini 复制代码
fn main()
{
    let variable = 5;
    let variable = variable + 1;

    {
        let variable = variable * 2;
        println!("{}", variable);
    }

    println!("{}", variable);
}

首先,数值 5 被绑定到变量variable。然后,通过使用let variable =重新声明并遮蔽了前面的variable,将其值更新为原值加 1,因此variable的值变为了 6。第三个 let 语句再次遮蔽了前面的variable,取用之前的值并乘以 2,最终variable的值变为 12。当运行此程序时,将输出以下内容:

shell 复制代码
$ ./shadowing
12
6

这种遮蔽机制与mut变量的使用有着显著的不同。mut允许我们在同一内存地址上修改变量的值,而不会发生内存对象的再分配,因此在性能上通常更优。而使用遮蔽时,每次let声明都会生成一个全新的变量,尽管它们恰好拥有相同的名称,但这实际上涉及到了内存对象的重新分配

变量遮蔽的用处在于,当我们在某个作用域内不再需要之前的变量(一旦被遮蔽,我们将无法再访问到之前的同名变量)时,可以重复使用变量名,这有助于避免绞尽脑汁去构思新的变量名,使代码更为简洁和清晰


结语

如果本文有任何问题欢迎在评论去指出,如果喜欢这篇文章,希望能点赞评论关注

如果你们身边有像你提起过这个领域的,或者希望可以和ta一起进步的,把这篇文章分享给ta吧

本文共4360字

本文作者与CSDN博主Cat Bayi作者为同一人,CSDN本文了链接


快捷翻页

Rust教程:How to Rust-从开始之前到Hello World


本文参考文献

Rust圣经

文心一言

相关推荐
Amagi.几秒前
Spring中Bean的作用域
java·后端·spring
2402_8575893623 分钟前
Spring Boot新闻推荐系统设计与实现
java·spring boot·后端
J老熊32 分钟前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构
Benaso35 分钟前
Rust 快速入门(一)
开发语言·后端·rust
sco528235 分钟前
SpringBoot 集成 Ehcache 实现本地缓存
java·spring boot·后端
原机小子1 小时前
在线教育的未来:SpringBoot技术实现
java·spring boot·后端
吾日三省吾码1 小时前
详解JVM类加载机制
后端
努力的布布1 小时前
SpringMVC源码-AbstractHandlerMethodMapping处理器映射器将@Controller修饰类方法存储到处理器映射器
java·后端·spring
PacosonSWJTU2 小时前
spring揭秘25-springmvc03-其他组件(文件上传+拦截器+处理器适配器+异常统一处理)
java·后端·springmvc
记得开心一点嘛2 小时前
在Java项目中如何使用Scala实现尾递归优化来解决爆栈问题
开发语言·后端·scala