rust变量声明

rust学习-2

写在前面的话
从今天开始就要开始正式进入到rust的基础学习了,上一篇文章写的可能比较笼统,只是简单的写了一些刚接触rust的一些工作,包括如何在linux下去安装rust环境,以及用什么来进行编辑代码,如何编译一个rust的可执行文件。接下来就要开始如何去声明一个变量,然后如何来打印出来这个变量,以及对这个变量进行赋值修改等一系列操作。

变量的声明

在学习go语言前,我们或多或少的都接触过一些其他语言,比如说 C/C++ 、 golang、java、Python 等等,一系列的语言,以golang为例,我们声明一个变量有两种方式:

// 1. 直接声明一个变量 
var a int64 = 1   // 通过这种方式我们可以声明一个 整形变量a,并且 a的值为1.
// 2. 使用 ‘:=’ 来声明一个变量

b := 1   // 通过这种方式我们也声明了一个整形变量 b ,并且b的值为1 ,但是值得注意的是 此时b的类型是int的而并不是 int64,后续使用的时候可能需要强转。

当然如果以 C/C++ java这些语言就需要如下的方式:

// 声明一个整形变量,
int a =  1;   // 我们需要这种方式来进行声明一个变量。

通过看以上几种语言的变量声明的方式,或许我们已经知道了,在rust中声明一个变量的方式也逃不出这几种,当然我们这里只讨论强类型语言的变量声明方式,不讨论Python这种弱类型的语言。
诚然在rust中也是需要一个类似 golang中的var的一个变量来进行声明一个变量,这个关键字就是

关键字:let
此时我们声明一个变量就可以用如下的方式:

fn main(){
	let a = 1;
    println!("a = {}", a)
}

执行以后得结果如下图所示:我们可以打印出a的值为1 。
在这里插入图片描述
声明出来一个变量以后,我们肯定想到的就是 修改这个变量的值,比如修改a的这个值为2。那么我们可以写出如下代码:


fn main() {
    let a = 1;
    println!("before: a = {}", a);
    a = 2;
    println!("after:a = {}", a);

}

在这里插入图片描述
那么执行以后会发生什么呢? 请看:
从上图中我们可以到,在修改a的数值为2以后我们发现居然失败了,这里面就涉及到这里面一个概念就是所谓的变量绑定,那么何为变量绑定呢,我们来继续看。
在rust中有一个最核心的原则就是——所有权,在rust中,任何一个内存对象都是有主人的,一般情况下就是完全的属于这个主人了,而绑定也就是把这个内存对象绑定到let声明出来的变量上,而在rust中,变量在默认情况下又是不可变的,如果重新赋值就会发生上面的错误,如果仔细看上面的错误的话,我们会发现他的错误原因是:

cannot assign twice to immutable variable x(无法对不可变的变量进行重复赋值)

rust中这种错误是为了避免一些无法预期的错误发生在我们的变量上,在我们日常的编程中也会遇到,一个变量a 在这里认为没有发生变化,直接获取的它的值,但是在某个未知的角落有一段代码却修改了这个值,从而导致了一些不可预知的错误,曾经工作中的一个好朋友就曾提过,在他维护的项目中偶先并发修改map的情况,或许熟知golang的朋友们知道,在golang中如果并发的修改了map的某一个key就会出现panic导致服务挂掉,最后根据panic信息和一步步的debug发现,在某一个未知的角落有人曾经提了一段代码在并发的情况下悄悄修改了这个值导致的。
rust为了规避这种问题,就将变量认为在默认情况下是不可以修改的 也就是只有let关键的时候。而这种限制实际上也会使得我们的代码会清晰一些,只有我们想让变量修改的时候才会修改。或许我们可以认为是 golang中的const。
但是如果变量不可变,我们每次修改变量的时候都要重新生成一个变量的话,那对于一个高并发的项目,拥有大量的对象场景,内存拷贝的成本就会特别高,性能也会变得非常低。因此变量的可变性也是需要有的
在rust中创建一个可变的变量是很简单的,我们看上面的截图也能看到,在报错中有一行写到
help: consider making this binding mutable: mut a
也就是说我们可以通过在声明变量的时候前面增加一个mut即可。

插一句题外话,rust在编译以后得报错确实很友好,还有一些帮助性的信息,能够帮助我们去修改代码。

好的,我们继续来看代码:来使用mut声明一个变量。

    let mut a = 1;
    println!("[before] a = {}", a);
    a = 2;
    println!("[after] a = {}", a);

在这里插入图片描述
此时我们发现就可以完美执行了。
当然在使用mut的时候也要带着 let ,需要 let mut a = 1; 才可以,不然还是会报错的哦。

变量忽略

在日常的开发中,我们可能会创建一些变量,但是我们不会再任何地方去使用,可能大家会觉得既然不用我们会什么不删掉它呢,但是在一个项目的最开始,我们可能会提前创建一些变量但是并不会使用,这个时候我们就希望告诉编译器,可以忽略这个未使用的变量,因此我们可以使用’_’ 来作为变量名的开头 。
代码示例:

fn main() {

    let a = 1;
    let _b = 2;

}

我们可以使用cargo run来执行以下,可以看到执行后结果如下:
在这里插入图片描述

我们可以看到,我们声明了a, b两个变量,都没有被使用,但是单独提示了 变量a没有被使用的警告,可以看到’_’ 变量名前缀的作用。
而且我们也可以看到,rust编译器的强大之处,可以直接给出修改意见,相信在后续我们能够越来越多的见识到rust编译器的强大。

变量结构

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);  // 用于断言 a 和 b是否相等,如果不相等会提示panic
}

在这里插入图片描述
我们通过上述代码可以看得出,通过let声明了两个变量a,b 通过元组的方式解析出a和b的值。并且b是一个可变的变量,通过assert方法来断言a,b 是否是相等的,
在执行的时候我也发现了一些别的问题,当我声明b是一个可变的变量时,如果我不去修改b的值会在编译时给出警告提示我说:建议去掉关键字mut;
在这里插入图片描述
在这里我们可以清楚的看到 如果我声明了mut但是没有修改变量的值,会提示我们需要去掉mut,通过看了一些评论和博客,我理解这或许也是rust的一种优化,rust的高性能来自于编译期就确定好变量的值,对于不变的变量,我们可以直接声明let而不需要再runtime的时候再去进行检查,对于上述代码如果我们后面不改变b的值的话吗,如果还声明成mut的话,就会在runtime时期会有一些多余的检查,而导致性能下降。当然这只是我个人的理解,或许随着更多的学习,也会有不同的理解。
从上面的执行结果我们还可以看到,如果a 和 b的值不相等的话 就会出现一个panic 来停掉我们的程序。panic 在一个程序中是比较严重的问题,它会直接导致进程崩溃退出,对于线上项目,我们也是不希望出现panic的。当然测试demo嘛。我们只是用它来检测一些东西。

变量与常量

在其他语言中我们也常常会声明一些不变的变量,用来在程序中进行使用。就是其他语言里面的关键字const。在rust语言里面 也是用const来进行声明一个常量。但是 我们上面也曾提到,let声明的一个变量也是不可变的,那么为什么还会有const这个东西呢。let和const主要有两个差别:

    1. const不能使用mut,也就是说在默认不可变的基础上,是自始至终都不会再发生改变,但是let我们可以再使用let 来给已经声明过的变量重新进行绑定。
    1. 常量的声明必须要有类型标注,也就是必须是确定的类型,即 整型,字符串等等,都是要标注好的。

声明一个常量可以使用如下:

const MAX_NUM: u32 = 10000;

通过上面的语句我们声明了一个整型的常量MAX_NUM 它的值是 10000;至于常量其他的用法与其他语言都是类似的,这里就不做赘述了,后续如果有机会能用rust来实现一个项目的话,肯定会用到常量的。我们也能更加深刻的去理解常量在rust中的意义。

在上面说constlet的区别的第一点里面,我们提到let 可以对一个已经声明过的变量重新进行绑定,这个这rust中叫做:
变量遮蔽
废话少说,直接上代码:

fn main() {
    println!("变量遮蔽");
    let a = 1;

    println!("[before] a = {}", a);

    let a = "world , hello~";
    println!("[after] a = {}", a);

    
}

执行结果:
在这里插入图片描述
执行上述代码以后,我们可以看到,a在最开始我们给他绑定了一个值为1 ,后面又将值绑定为了一个字符串。这个操作就和mut的完全不同了,mut实际上是修改了同一个内存上值,但是上述的代码是 生成了一个完全不同的一个新变量,内存也是重新分配的,只是这两个的变量名字恰好是同一个名字罢了。
或许我们可以用mut声明一个变量,然后将其改为不同类型的值来看下执行以后会不会有报错:
上代码:

fn main() {
    println!("变量遮蔽");

    let mut a = 1;
    println!("[before] a = {}", a);
    a = "world, hello ";
    println!("[after] a = {}", a);

}

只看代码,我们就知道,这个一定会报错,相当于是给一个int类型的变量,又赋值了一个string类型的。我们来看看rust的编译器会给我报什么错误呢?
在这里插入图片描述
我们可以看到,给我们报了类型错误,即期望是一个整型,但是我们赋值了string类型。

总结

通过以上,我们分析了let 关键字的作用,包括在rust中如何声明一个变量。对于可变的变量要增加关键字mut, 以及let变量可以进行变量遮蔽,给一个变量重新进行绑定到一个内存上,还有常量的定义和使用等等。或许本文写到这里也不是一个最终版,随着后面的学习,逐渐加深对rust的理解以后还会对本文进行重新进行编辑。

  • 9
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值